JPOX EMF Resources
- Resource URI
- JPOX Resource
- Sharing one PersistenceManager between Resources, One transaction when saving multiple resources
- Closing a resource
- Validation
- Standard load behavior: Top level types
- Customizing load behavior
- Load of referenced Objects
- Closing a resource
Resource URI
The integration layer automatically registers the JPOX resource for the protocol: jpox and jpoxdao. The jpoxdao protocol is deprecated and present because of backwards compatibility.
From the 0.7.5 release the EMF-JPOX integration offers one type of resource: JPOXResource. The previous JPOXResourceDAO is no longer used. The jpox protocol will from now return a JPOXResource instance.
The JPOX resource needs to know how to reach a data store. This is done by passing the name of a JpoxDataStore to the resource with the JPOXResource.DS_NAME_PARAM parameter. The name of the JpoxDataStore is the name used when creating/registering a JpoxDataStore using the JpoxHelper.INSTANCE object.
An example of a URI which returns a JPOX resource: jpox://dsname=MyPMF.
JPOX Resource
The JPOX Resource (org.eclipse.emf.teneo.jpox.resource.JPOXResource) starts a transaction in the load action and commits and reopens a transaction for the save action.
This behavior has the following consequences:
- The assumption is that optimistic transactions are enabled.
- After saving the resource the saved objects can still be used by the application, so no refresh is required.
- ELists are lazily loaded, primitive type fields and single EReferences are loaded when the resource is loaded.
Sharing one PersistenceManager between Resources, One transaction when saving multiple resources
As a standard behavior the resource will create its own PersistenceManager (pm) at load time. However, there are cases when you want to use one pm 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 pm and be done in the same transaction.
To support this Teneo has the concept of a PMController. A PMController manages one pm for multiple resources. A PMController is registered using a specific name. When a resource is opened this name is passed as an uri parameter. Using this name the JPOXResource can then find the PMController and retrieve a pm. When a JPOXResource has a PMController then the JPOXResource does not itself create a pm or begin and commit transactions. Beginning and committing transactions is the responsibility of the application itself.
Here is some example code illustrating the use of a PMController:
PMController pmc = new PMController(); // when creating a PMController you have to pass your JpoxDataStore instance pmc.setJpoxDataStore(jpoxDataStore); // register the PMController, the name is used in the uri of the resource PMController.registerPMController("testsc", sc); // create an uri using the PMController name URI uri1 = URI.createURI("jpox://?" + JPOXResource.SESSION_CONTROLLER_PARAM + "=testsc&query1=select from " + BookImpl.class.getName()); URI uri2 = URI.createURI("jpox://?" + JPOXResource.SESSION_CONTROLLER_PARAM + "=testsc&query1=select from " + WriterImpl.class.getName()); // resourceSet has to be set before somehow.. final Resource res1 = resourceSet.createResource(uri1); final Resource res2 = resourceSet.createResource(uri2); // now load the resources pmc.getPM().currentTransaction().begin(); res1.load(Collections.EMPTY_MAP); res2.load(Collections.EMPTY_MAP); pmc.getPM().currentTransaction().commit(); // do something usefull with your loaded resources... // and save them pmc.getPM().currentTransaction().begin(); res1.save(Collections.EMPTY_MAP); res2.save(Collections.EMPTY_MAP); pmc.getPM().currentTransaction().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 JPOXResource can be closed by calling the unload method. This method will close the PersistenceManager. In case a PMController is used the unload method will just nullify the internal pm member, you should close the pm explicitly.
Validation
To enable validation on a JPOX Resource you need to call setTrackingModification(true) on the resource. The JPOX 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 an org.eclipse.emf.teneo.StoreValidationException. This exception has a method to retrieve all Diagnostics.
Standard load behavior: Top level types
When a EMF/JPOX resource is loaded (without using the query customizations) 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 Known Issues.
Customizing load behavior
It is possible to customize what the resource actually loads from the database.
Extend the resource implementation with a Java subclass
It is possible to customize the load behavior by overriding the loadFromStore method in JPOXResource. This method gets a PersistenceManager and should return a list of objects read from the JDO/JPOX datastore.
Set specific queries to load the resource
There are two ways to pass specific queries to the JPOXResource. The first method is using uri parameters:
jpox://?dsname=mystore&query1=SELECT FROM org.eclipse.example.library.impl.WriterImpl
Two things are important here: 1) for JDO the query should fully qualified classnames to the concrete implementation classes, 2) you can pass multiple queries by using parameter names which start with query, for example query1, query2 etc.
The other way to pass queries to the resource are to specify them in the .ejdo file (see resource utility here). An example (commented out) of this can be found in the ejdo file here.
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 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. Eventhough the referenced objects are not directly present in the contents the eResource of the referenced objects is set to the loading resource. This is only done if objects are not already present in another resource.
- StoreResource.ADD_TO_CONTENTS: this value means that any referenced objects and the container of a loaded object (if resolveProxies==false for the containment relation) are added to the contents of the resource. This means that these objects are present in the top of the resource. If resolveProxies==true then the container of a loaded object is not loaded in the resource as 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.
Closing a resource
The EMF/JPOX resource can be closed by calling the unload method. This method will close the used Persistence Manager.