HomeEMF HibernateEMF JDO/JPOXWeb App GenerationServices
 

EMF - JPOX Integration Details

Discusses a number of specific implementation and use details of the EMF - JPOX integration.

Requirements on EObjects: InternalEObject

The EMF JPOX persistency layer only requires that the persisted objects implement the org.eclipse.emf.ecore.InternalEObject interface.

Requirements on EPackages

The OR Mapper searches for the EPackage classes in the project and uses the classpath of the project of the selected ecore file. The only requirement is that the EPackage classes (the generated interface) extend the EMF EPackage interface directly.

Persisted EMF Objects after Commit

When EMF objects have been persisted and the transaction is committed then these objects can not be changed anymore. Only read access is possible. Please refer to the relevant JDO documentation about this topic.

Classloader

Teneo performs explicit classloading in specific locations. It is possible to set the classloader which is being user by Teneo. This can be done through the org.eclipse.emf.teneo.classloader.ClassLoaderResolver class. In this class you can register a org.eclipse.emf.teneo.classloader.ClassLoaderStrategy. The registered ClassLoaderStrategy is used by Teneo to explicitly load classes.

Custom Types

You can register your own TypeMappings through a call to org.jpox.TypeManager.addType. This needs to be done during initialization of the Persistence Manager Factory. To accomplish this you need to override JPOXDataStore and the method addCustomTypes(...). Then you can register your own typemapping as follows:

tm.addType(List.class.getName(), EListMapping.class.getName(),
		EListWrapper.class.getName(), false, "1.4", true, false, false, clr);
			

See the jpox documentation here, it is not necessary to use the xml file to register type mappings, the call to the TypeManager above will also work.

Move an EObject between EContainers or support cut and paste in the EMF editor

In the standard approach it is not possible to move an EObject from one containment relation to another containment relation. A move between containment relations corresponds to a cut and paste in the EMF editor. The reason is that Teneo will specify an dependent-element="true" for a containment relation. This has as a consequence that JPOX will throw an exception (Cannot write fields to a deleted object) when you move an EObject from its container to another container.

Solution: as a solution you can prevent the dependent-element from being set, by specifying an annotation on the relevant EReference features. As an example:

<xsd:element maxOccurs="unbounded" minOccurs="0" name="books" type="lib:Book">
	<xsd:annotation>
		<xsd:appinfo source="teneo.jpa">@OneToMany(cascade={MERGE,PERSIST,REFRESH,REMOVE})</xsd:appinfo>
	</xsd:annotation>
</xsd:element>
			

This annotation means that cascading deletes are still enforced but a child (the Writer) can exist without its parent (the Library).

When this annotation is used in the context of a JPOX Resource then when an EObject is removed from its container then it will also be removed from the resource and from the database. However, when you set this annotation and not work with JPOX Resources then the removed EObject is not removed from the database and will be present without a container!

Validation

When storing and retrieving EMF objects from a JPOX store it is not required to work with a EMF type Resource. However the standard EMF validator checks if every EObject is present in an EMF resource and that all referenced EObjects are in the same resource. So, if this standard validator is used unnecessary errors are thrown.

To prevent this situation you can register your own validator which does not perform this resource check. For your convenience it is also possible to use the org.eclipse.emf.teneo.jpox.validation.JPOXValidator. See its source code below.

Validators are registered using a call to put method of the EValidator.Registry.INSTANCE object.

			
public class JPOXValidator extends EObjectValidator
{
	/** 
	 * Overrides the method from the superclass to prevent this check because it
 	* is not required in the context of a jdo store. Note that this assumes that 
 	* an object and its references are all stored in the same jpox database. 
 	*/
	public boolean validate_EveryReferenceIsContained(EObject eObject, DiagnosticChain diagnostics, 
		Map context) 
	{
		return true;
	}
}
			

Force a join table for 1:n relations

In a relational database 1:n relations are often modeled by adding a foreign-key (to the parent) in the child table/object. The disadvantage of this approach is that duplicates are not supported. To support duplicates in an elist a join table is required. To signal to Teneo, that a join table should be used, the annotation as in this example can be used:

<xsd:element name="joinedItem" type="xsd:anyURI" ecore:reference="this:Item" maxOccurs="unbounded">
	<xsd:annotation>
		<xsd:appinfo source="teneo.jpa">@OneToMany(unique=false)</xsd:appinfo>
	</xsd:annotation>
</xsd:element>
			

Based on this annotations Teneo will generate a join table for this model. Note that this annotation can be combined with the indexed annotation.

The unique annotation does not exist in the EJB3 spec and is a Teneo extension.

JPOX/Relational Validation and EObject persistency

When an object is made persistent using JPOX then it is almost always directly written to the backing relational store. At that moment also constraints, such as nullable fields, are checked. This means that only valid EObjects should be made persistent using the PersistenceManager makePersistent call.

An object is also made persistent when it is added to the collection of a persistent object. So only valid objects should be added to the collection of a persistent object. See the example below.

The following source will result in an error because the Book object does not have its title set when it is added to the library.

Book book = LibraryFactory.eINSTANCE.createBook();
lib.getBooks().add(book); // ERROR, here the book does not have a title yet!
book.setAuthor(writer);
book.setPages(305);
book.setTitle("The Hobbit");
book.setCategory(BookCategory.SCIENCE_FICTION_LITERAL);
		

To make this valid, the book should be added to the library after all its values have been set.

PersistenceManagerFactory configuration

PersistenceManagerFactories should be created through a call to the relevant methods in the JPOXHelper.INSTANCE object. If only the database connection information is passed then the JPOXHelper will use the configuration properties below. for more information see the jpox site here.

properties.setProperty(PMFConfiguration.JDO_IGNORECACHE_PROPERTY, "false");
properties.setProperty(PMFConfiguration.JDO_RETAINVALUES_PROPERTY, "true");
properties.setProperty(PMFConfiguration.JDO_NONTRANSACTIONAL_READ_PROPERTY, "true");

properties.setProperty(PMFConfiguration.STRING_DEFAULT_LENGTH_PROPERTY, "255");

properties.setProperty("javax.jdo.PersistenceManagerFactoryClass", "org.jpox.PersistenceManagerFactoryImpl");

// set it all to false because of performance
properties.setProperty(PMFConfiguration.AUTO_CREATE_SCHEMA_PROPERTY, "false");
properties.setProperty(PMFConfiguration.AUTO_CREATE_CONSTRAINTS_PROPERTY, "false");
properties.setProperty(PMFConfiguration.AUTO_CREATE_SCHEMA_PROPERTY, "false");
properties.setProperty(PMFConfiguration.AUTO_CREATE_TABLES_PROPERTY, "false");
properties.setProperty(PMFConfiguration.VALIDATE_COLUMNS_PROPERTY, "false");
properties.setProperty(PMFConfiguration.VALIDATE_CONSTRAINTS_PROPERTY, "false");
properties.setProperty(PMFConfiguration.VALIDATE_TABLES_PROPERTY, "false");

properties.setProperty(PMFConfiguration.CACHE_LEVEL_1_TYPE_PROPERTY, 
	"org.eclipse.emf.teneo.jpox.cache.EMFWeakRefCache");
properties.setProperty(PMFConfiguration.METADATA_JDO_FILE_EXTENSION_PROPERTY, 
	JpoxHelper.INSTANCE.getJdoFileExtension());
		

For the EMF - JPOX integration it is very important that as a level1 cache one of the cache implementations found in org.eclipse.emf.teneo.jpo.xemf.cache is used.

The default String length is set to prevent a bug in MySql, the JDO default is 256.

The AUTO_CREATE_* and VALIDATE_* properties are set this way for performance reasons. At initialization time the JpoxDataStore will override these properties to update the database schema, see also here options.

You can set the file extension to use through the JpoxHelper.INSTANCE.setJdoFileExtension, this allows you to more easily test multiple package.jdo files.

The current integration will not work properly if one of the following settings is used:

  • org.jpox.delayDatastoreOperationsUntilCommit to true does not work with bi-directional references
  • javax.jdo.option.Optimistic set to true does not work with bi-directional references. The current EMF/JPOX integration works with pessimistic transactions. However, in the current JPOX version records are not locked in the database (see).

Fetch Group

A FetchGroup defines which fields are read when an object is loaded from the data store. The EMF - JPOX integration sometimes overrides the working of a FetchGroup because containment references are always read.

Direct read of contained objects, container feature not set

When an object is contained and is read directly (using a JDO query) then its container feature is not set. For example, a Book is contained in the Library (see tutorial). If a jdo query reads all books then these books EMF objects do not have their container set. If a library object is read which contains books then the books will have their container feature set.

Since the 0.6.3 release the container can be set explicitly after retrieving the contained EObjects directly from the database, see here.

Registering a Persistence Manager Factory Creator

You can register your own persistence manager factory creator class. This class is called by Teneo to create a PersistenceManagerFactory. By registering your own PersistenceManagerFactoryCreator, you can override the creation of persistence managers etc.

The persistence manager factory creator should implement the interface: org.eclipse.emf.teneo.jpox.PMFCreator. The creator can be registered using the method JpoxHelper.INSTANCE.setPMFCreator(....).

Lazy loading and proxy objects

As a default when an object is loaded from the JPOX store only the primitive type fields are read immediately. Reference fields and collections are lazely loaded, only when required. This behavior is very similar to the proxy object behavior of standard EMF resource implementations.

If even 'more lazy' behavior is required then it is possible to set the fetch group on the PersistenceManager, see

Hiding class names, using interfaces

EMF is very interface oriented and tries to hide the generated implementation classes. JPOX on the other hand requires the use of the implementation classes in queries. The runtime layer supports the conversion of an EMF interface name to a implementation class through the JpoxHelper.INSTANCE.getInstanceClass method. With this it is possible to work with EMF interfaces without using the implementation class names directly.

Not Supported by JPOX

Lists of user-defined primitive types are not supported, an example is a list of enumerates