Teneo/Hibernate/ModelRelational/Entity Attribute Value Mapping
Teneo supports the Entity-Attribute-Value mapping model. In this mapping all the data is stored in just two tables (the object and its values). This compared to the standard domain-specific mapping in which the data is stored in tables reflecting the domain model.
The main advantage of an EAV mapping is that the database schema does not change when the model changes. This means that EAV mapped models can be much more dynamic and can be more easily changed at runtime.
The Teneo EAV mapping supports EMF features such as dynamic-EMF, Featuremap, EMaps and all tested XSD features. Also cascading deletes and delete-orphan for containment relations are supported out-of-the-box.
An important feature is that the EAV Mapping can be combined with a standard Teneo mapping. This means that part of your model can be stored as an EAV mapped model while other parts are stored in a database schema that reflects the domain model.
Teneo/Hibernate take care of transparently mapping references between the EAV and standard mapped model. Objects stored in the EAV part of the database can refer to objects stored in the standard domain database tables and vice versa. This cross-referencing is supported for both single references as multi-references (lists) and for containment and non-containment associations. When loading and persisting objects Hibernate takes care of storing the objects in their respective tables and resolving relations.
An EAV mapping means that certain features work differently or are disabled compared to the concrete domain-specific mapping, such as a less-readable database model and very complex querying. See the limitations section below for more information.
Note as the support for EAV is fairly new (august 2009) it is possible that certain features have not been tested thoroughly.
Enabling EAV Mapping
The EAV mapping can be enabled in two ways:
- the teneo.mapping.eav_mapping: if this option is set to true then all types are mapped using the EAV database schema. Note that you can override this for specific EClasses using the NoEAVMapping annotation.
- the EAVMapping annotation: by adding the annotation @EAVMapping to an EClass, the EClass and all its child EClasses are mapped using the EAV schema.
The option sets the EAV mapping globally, while the annotation allows you to handpick which types are mapped using an EAV mapping.
Queries using just the id or queries which retrieve all objects of a certain type are the same as with standard hibernate:
SELECT e FROM Employee e OR SELECT e FROM Employee e WHERE e.id=:id
Also the hibernate session.get(...) methods work fine with EAV stored objects.
However if you want to use object features/values in queries then this becomes more complex.
For example the following code snippet shows how to query for a Library (from the famous EMF example) with a certain name:
final Query qry = session.createQuery("select eav.owner from EAVSingleEAttributeValueHolder eav where eav.feature=:feature and eav.stringValue='Science_Fiction'"); qry.setParameter("feature", LibraryPackage.eINSTANCE.getLibrary_Name());
The EAV mapping itself consists of a set of EAV classes, supporting different EMF concepts, and the mapping of these classes to the EAV database schema. The EAV classes can be found in the org.eclipse.emf.teneo.hibernate.mapping.eav package in the org.eclipse.emf.teneo.hibernate plugin. The hibernate mapping model of the EAV schema can be found in the eav.hbm.xml file located in the same package/plugin.
Note: the EAV classes are only used to store and retrieve information from the database through hibernate. Hibernate methods will return the actual (generated) domain objects and when calling Hibernate the actual (generated) domain objects can be used.
You can customize the EAV mapping by providing your own hbm mapping file. See the option EAV_MAPPING_FILE to the file location.
The first limitation is that the Hibernate session.merge method will not work for EAV objects. As a workaround Teneo provides a generic merge method (see the HbUtil.merge method). This merge method uses the EMF api to traverse an objects values and copy them over to an eobject from the database. The method can also easily be implemented yourselve for custom behavior in specific cases (check the source code, it is a short method).
The main limitation of the Entity-Attribute-Value model is that querying for data using HQL queries is much more complex.
Instead of the meaning-full domain model concepts, the HQL queries should use the mapping model of the EAV schema. The hibernate mapping model of the EAV schema can be found in the eav.hbm.xml located in the org.eclipse.emf.teneo.hibernate.mapping.eav package in the org.eclipse.emf.teneo.hibernate plugin. The mapping is more complex then the actual database schema because EMF concepts as FeatureMap and EMap are supported.
Another limitation is that database foreign key constraints are not enforced for references to and from the EAV mapped objects. All references are done using the hibernate any/many-to-any mappings. However, as all references are stored together in one entity it is easy to check/test if there are objects being refered to. The following two queries will give all the refering objects which refer to the referedTo object:
SELECT eav.owner FROM EAVSingleNonContainmentEReferenceValueHolder eav WHERE eav.referenceValue=:referedTo SELECT eav.owner FROM EAVSingleContainmentEReferenceValueHolder eav WHERE eav.referenceValue=:referedTo
With these two queries it is easy to implement delete constraint checking in pre-delete interceptors in Hibernate.
Another limitation is that an EAV mapping does not support custom primary key/ID types. All id's are generated long values.