Inheritance Mapping in EJB3

Supported inheritance mapping strategies

The Teneo OR Mapper supports all three inheritance mapping of Hibernate (see chapter 9 of the Hibernate manual):

* SINGLE_TABLE: Table-per-class-hierarchy, in this scenario the classes of one class hierarchy are all mapped to one table.
* JOINED: Table-per-subclass, in this strategy each subclass will have its own table. To retrieve an object from the database the superclass and subclass tables are joined. This also applies to subclasses of subclasses.
* TABLE_PER_CLASS: table-per-class, each concrete class is stored in its own table. All properties (including inherited) are mapped to the table of the concrete class. Teneo uses the union-subclass concept of Hibernate to map table-per-class. The disadvantage is that generated id's are not supported. If you prefer the table-per-class implicit polymorphism approach of Hibernate then this can be (in a way) achieved by setting the mapped superclass annotation on the superclass. Note that with mapped superclass the superclass itself will not be persisted separately.

As a default SINGLE_TABLE inheritance mapping is used. There are two methods by which the inheritance mapping can be set.

1. Globally: pass the constant org.eclipse.emf.teneo.mapper.PersistenceMappingOptions.INHERITANCEMAPPING as a property when initializing a HbDataStore. This property can have two values: SINGLE_TABLE or JOINED.

2. By using an (EJB3) annotation for individual types: see below.

Hibernate has as main limitation that the two approaches above can not be mixed within one class hierarchy.

There is also another way of mapping inheritance structures: MappedSuperclass. In this method the superclass is not persisted by itself and does not have its own table. All the properties of the superclass are stored in the table(s) of the subclass(es). MappedSuperclass is set by an EJB3-like annotation. For more information see the EJB3 spec.
Specifying inheritance mapping per class hierarchy

There are cases where it is necessary to use different inheritance mapping techniques for different class hierarchy within one application or project. This can be achieved by adding specific EJB3-like annotations to the type definition (uml or XML Schema).

As an example see the following type definition of Address which sets the inheritance mapping strategy to JOINED (joined subclass) for Address and all its subtypes:



@Inheritance(strategy=JOINED)



The inheritance-mapping annotation can have one of the two following values: JOINED or SINGLE_TABLE.

The above annotation can also be added in UML. Please use the same values for the source and the key.

The inheritance annotation has changed, from the 0.3.* to the 0.7.0 release, to an EJB3-like format.
Multiple Inheritance Support

In EMF it is possible to define multiple inheritance structures. Although Hibernate itself does not support multiple inheritance Teneo does its best to map a multiple inheritance structure.
Choosing the real supertype

The following approach has been implemented in Teneo. During the mapping the first (non-MappedSuperclass, non-interface) supertype is considered to be the real parent (for Hibernate). If the type only has interface supertypes then the first one of these (which is not a mappedsuperclass) is chosen. The properties of the other super types are mapped together with the subclass. These other supertypes are treated as a MappedSuperclass (see the jpa/ejb3 spec). The other super types will have their own tables in the database but there will be no relations between the subclass table and its other superclass tables.

See as an example the figure below, the figure illustrates how a model is translated to a relational database schema using a joined-subclass inheritance mapping strategy. The SchoolBook inherits from both Book and Asset. This is translated into a relational model with SchoolBook refering to the chosen real supertype (Book) and also inheriting the value-efeature from Asset. This means that when the SchoolBook is read from the database then it will also implement the Asset interface/type and its value efeature is set correctly. On the other hand there is no relation to the Asset table (see limitation below).


Multiple Inheritance Example
Limitation

The main limitation is that references to supertypes do not (always) work correctly. Using the example above. According to the model and the generated code the AssetList can also contain SchoolBooks (as the type inherits from Asset). However, the database schema shows that a SchoolBook does not have a foreign key reference to the AssetList table. So when an AssetList with SchoolBooks is persisted the save action will fail.
The @Entity annotation, choosing a different supertype for relational mapping

As described above Teneo, as a default, chooses the first supertype as the one to use to generate the relational mapping. However there are cases (see here for example) that this is not desired and it is neither possible to change the supertype definition. In this situation you can use the @Entity annotation and its extends field. The extends field should be set to the entityname of the desired supertype. See the this annotations.xml as an example.
Other inheritance mappings: single-table, table-per-concrete-class, implicit polymorphism, any-reference

The example above uses a joined-subclass strategy. With single-table basically the same limitation applies. With table-per-concrete-class other limitations apply as described in the hibernate manual (chapter 9, section 9.2).

Implicit polymorphism should also work in case of multiple inheritance (querying for the Asset interface should return also SchoolBooks). This has however not been tested with Teneo.

The reference in the example can also be mapped using the hibernate any-reference. By using the any-mapping an AssetList with SchoolBooks can be persisted. However, the any-mapping has as main disadvantage that no referential integrity is enforced.

0 comments: