The EMF Hibernate integration has two Resource implementations (HibernateResource and HibernateXMLResource) for integration with the EMF Editing Domain. Both resource implementations extend the org.eclipse.emf.ecore.xmi.impl.ResourceImpl. The HibernateXMLResource extends the HibernateResource and in addition implements the XMLResource implementation.
The Hibernate Resource is registered using different extensions/protocols:
- Protocol: the Hibernate EMF Resource (factory) will be registered for the protocols hibernate and ehb, the HibernateXMLResource is registered with the protocol hbxml.
- Extension: the Hibernate EMF Resource (factory) will be registered for the extensions hibernate and ehb
When the hibernate protocol is used to retrieve a Resource then the resource needs to know how to reach a data store. This is done by passing the registration name of a HbDataStore to the resource with the org.eclipse.emf.teneo.Resource.DS_NAME_PARAM parameter.
An example of a URI which returns a Hibernate resource: hibernate://?dsname=library.
Hibernate Resource and session/transaction specifics
The Hibernate Resource opens a session when it is loaded for the first time. A transaction is started when the load starts and committed at the end of the load. This session is disconnected at the end of the load action.
The save action will reconnect the session, begin a transaction, do an update of the datastore and then commit and disconnect.
To enable validation on a Hibernate Resource you need to call setTrackingModification(true) on the resource. The Hibernate Resource then validates its content (the EObjects) when the resource is saved. This validation makes use of the org.eclipse.emf.teneo.resource.NonLoadingDiagnostician. This is a subclass of the standard EMF Diagnostician which does not load unloaded lazy lists. Only lists with minOccurs > 0 are loaded during validation. Validation fails when a ERROR level Diagnostic is encountered. In this case the resource save method will throw a org.eclipse.emf.teneo.StoreValidationException.
This exception has a method to retrieve all Diagnostics.
Standard load behavior: Top level types
When a Hibernate EMF resource is loaded then only the so called top-level types are directly present in the resource contents. Top-level types are types which are not used as the the child in a containment relation. The assumption is that all contained types can be reached from a top-level type.
See also the FAQ.
Customizing load behavior
It is possible to customize what the resource actually loads from the database.
Extend the resource implementation in java
It is possible to customize the load behavior by overriding the loadFromStore method in the HibernateResource. This method gets a PersistenceManager and should return a list of objects read from the Hibernate datastore.
Set specific queries to load the resource
There are two ways to pass specific queries to the HibernateResource. The first method is using uri parameters:
You can pass multiple queries by using parameter names which start with query, for example query1, query2 etc.
Load of referenced Objects
Objects can be loaded explicitly (by using queries) or they are loaded implicitly when a 'lazy' collection is loaded or there are references from explicitly loaded objects. The resource implementation can handle implicit loading of additional objects in different ways. The implicit load strategy is controlled by setting the StoreResource.LOAD_STRATEGY_PARAM parameter. This parameter can be passed as an option to the load method or as a parameter in the uri. It can have two values:
- StoreResource.SET_ERESOURCE (default): in this mode the explicitly loaded objects are added to the resource. In addition referenced objects are added to the resource and will be managed by the resource.
- StoreResource.NOT_SET_ERESOURCE: in this mode only the explicitly loaded objects are present in the contents of the resource (i.e. in the top of the resource content). Objects may be present as contained children of explicitly loaded objects. Objects references through non-containment relations are not added to the contents of a resource.
- StoreResource.ADD_TO_CONTENTS: any referenced objects (and their container) are added to the resource. So during resolving of a references it is possible that the content list of a resource is modified. When a referenced object is contained using a containment relation withresolveProxies==true, then the object itself is added to the root of the resource, as with resolveProxies the container may be in a different resource. Implicitly loaded objects are only added to the loading resource if they are not already present in another resource.
Implicit loading is mainly of importance when loading a resource using queries. In the default load method all top-entities are loaded anyway and then all objects are reachable through a loaded container.
Sharing one Session between Resources, One transaction when saving multiple resources
As a standard behavior the resource will create its own session at load time. However, there are cases when you want to use one session to load and save multiple resources. This is for example the case when there are references between objects in different resources. In this case the load and save actions of multiple resources should use the same session and be done in the same transaction.
To support this Teneo has the concept of a SessionController. A SessionController manages one session for multiple resources. A SessionController is registered using a specific name. When a resource is opened this name is passed as an uri parameter. Using this name the HibernateResource can then find the SessionController and retrieve a session. When a HibernateResource has a SessionController then the HibernateResource does not itself create a session or begin and commit transactions. Beginning and committing transactions is the responsibility of the application itself. The Teneo session controller will set the flush mode to manual. The hibernateresource will automatically flush at save.
Here is some example code illustrating the use of a SessionController:
SessionController sc = new SessionController(); // when creating a SessionController you have to pass your HbDataStore instance sc.setHbDataStore(hbDataStore); // register the SessionController, the name is used in the uri of the resource SessionController.registerSessionController("testsc", sc); // create an uri using the SessionController name URI uri1 = URI.createURI("hibernate://?" + HibernateResource.SESSION_CONTROLLER_PARAM + "=testsc&query1=from Book"); URI uri2 = URI.createURI("hibernate://?" + HibernateResource.SESSION_CONTROLLER_PARAM + "=testsc&query1=from Writer"); // resourceSet has to be set before somehow.. final Resource res1 = resourceSet.createResource(uri1); final Resource res2 = resourceSet.createResource(uri2); // now load the resources sc.getSession().beginTransaction(); res1.load(Collections.EMPTY_MAP); res2.load(Collections.EMPTY_MAP); sc.getSession().getTransaction().commit(); // do something usefull with your loaded resources... // and save them sc.getSession().beginTransaction(); res1.save(Collections.EMPTY_MAP); res2.save(Collections.EMPTY_MAP); sc.getSession().getTransaction().commit();
Loading different EObjects (refering to eachother) in different resources can have side-effects if there are containment relations to and from these EObjects (with resolving=false). If the containment relation is non-resolving then EMF will place an EObject always in the same resource as its container. If you use queries to load your resources then the containers are loaded also. For example: assume EObjects A1 and A2 both have container A0. If one resource loads A1 then A0 is also loaded automatically and A0 and A1 are in the same resource. If then a second resource loads A2 then A2 will be placed in the first resource because its container is located there. Overall this results in unpredictable behavior. So: to correctly work with multiple resources the containment relations should be set to resolving = true, in addition the genmodel property Containment Proxy needs to be set to true.
Closing a resource
The Hibernate EMF resource can be closed by calling the unload method. This method will close the disconnected session. In case a SessionController is used the unload method will just nullify the internal session member, you should close the session explicitly.
The HibernateXMLResource extends the HibernateResource and implements the XMLResource interface. However the XMLResource implementation is very limited at the moment. The HibernateXMLResource has been introduced for the GMF integration. For GMF the getID method was the most important. This method has been implemented. Other methods will be implemented with meaningfull functionality when it is requirement.
Resource use after Hibernate exception
When a Hibernate error/exception occurs when working with a Hibernate resource it can not be used anymore and should be unloaded (closed).
In a future resource implementation this issue will be solved.