Inheritance

The EJB 3.0 specification introduces inheritance:

An entity may inherit from another entity class. Entities support inheritance, polymorphic associations, and polymorphic queries.

Both abstract and concrete classes can be entities. Both abstract and concrete classes can be annotated with the Entity annotation, mapped as entities, and queried for as entities.

Entities can extend non-entity classes and non-entity classes can extend entity classes.

The following howto should give you a basic understanding on how to model your inheritance hierarchies using the EJB3 cartridge.

Non Interhiting Entities

In a mapped superclass scenario, an entity inherits from a superclass that has persistent state and mapping information, but the super class is NOT an entity and is not mapped to a relation table. This is the strategy employed by the EJB3 cartridge when generating non-inheriting entities if instance scoped operations exist on the entity.

A mapped superclass is generated containing almost all of the mapping information. The subclass contains only the @Entity , @Table and @EntityListeners annotations. All attribute and relationship mapping information exists in the mapped superclass. The mapped superclass is regenerated on every run, however the subclass is not.

Until we declare entity pojos via orm.xml instead of annotations, the inheriting subclass in the latter scenario will require manual modifications if you change certain metamodel tags.

Mapped Superclasses

To explicitly define an entity as a mapped superclass, model the <<Entity>> AND <<MappedSuperclass>> stereotypes on the class. (Note: The cartridge did attempt to avoid the need to model the <<Entity>> stereotype, however this caused issue with UML2 models)

You must explicitly define your identifier(s) either in the mapped superclass or in the subclass entity.

The following is an example of this type of inheritance where the Vehicle mapped superclass contains the identifier which is automatically used by the Car entity.

  • Auto-generated source that does not need manual editing
  • Auto-generated source that should be edited manually
  • File that is affected by the modifications applied in this section
  • Vehicle.java
  • CarEmbeddable.java
  • Car.java
  • CarType.java
  • CarDao.java
  • CarDaoBase.java
  • CarDaoImpl.java
  • CarDaoException.java
  • Person.java
  • PersonDao.java
  • PersonDaoBase.java
  • PersonDaoImpl.java
  • PersonDaoException.java
  • RentalServiceBean.java
  • RentalServiceRemote.java
  • ServiceDelegateBase.java
  • RentalServiceDelegate.java
  • RentalServiceBase.java
  • RentalServiceException.java
  • RentalException.java

It's worthwhile to note that only a Vehicle class is generated which contains all the mapping information. In this case, because the Vehicle class was explicitly defined as a mapped superclass, the naming pattern does NOT append Embeddable to the class name. This class is regenerated every time AndroMDA runs; any manual changes to this class will be overwritten.

The process also generates a CarEmbeddable mapped superclass, containing all mapping information, along with an extending Car implementation entity class. Remember that this only happens if the entity contains instance scoped operations. The naming pattern here dictates that Embeddable is appended to the mapped superclass. Remember this is NOT the case when you explicitly define the a mapped superclass.

The Person entity operates under normal EJB3 cartridge guidelines since there is no inheritance hierarchy for this entity. Therefore, only a Person class is generated and should not be modified since it will be overwritten during the next build.

If you are going to enable manageable entities, you must ONLY enable your identifiers in the subclass and not the parent mapped superclass.

Single Table Strategy

In this strategy, the complete class hierarchy is persisted to a single table. To differentiate between class types, a discriminator column is used. The discriminator column type is specified in the root class along with the inheritance strategy employed. All classes need to specify the discriminator value associated to each class in the hierarchy.

If the query is based on the root class in the hierarchy, the query is polymorphic which implies that entity subclass instances will be returned.

Pros: Good support for polymorphic relationships between entities and queries over the class hierarchy.

Cons: Columns corresponding to state of subclasses must be nullable.

The EJB3 cartridge will assume a single table mapping strategy when you model an inheritance hierarchy between entities, unless specified otherwise.

  • Auto-generated source that does not need manual editing
  • Auto-generated source that should be edited manually
  • File that is affected by the modifications applied in this section
  • Vehicle.java
  • VehicleDao.java
  • VehicleDaoBase.java
  • VehicleDaoImpl.java
  • VehicleDaoException.java
  • CarEmbeddable.java
  • Car.java
  • CarType.java
  • CarDao.java
  • CarDaoBase.java
  • CarDaoImpl.java
  • CarDaoException.java
  • Person.java
  • PersonDao.java
  • PersonDaoBase.java
  • PersonDaoImpl.java
  • PersonDaoException.java
  • RentalServiceBean.java
  • RentalServiceRemote.java
  • ServiceDelegateBase.java
  • RentalServiceDelegate.java
  • RentalServiceBase.java
  • RentalServiceException.java
  • RentalException.java

Notice in the previous example where Vehicle was defined as a mapped superclass, no DAO components were generated. In this example, Vehicle is an entity and the the corresponding DAO components were generated. The discriminator components are discussed below.

Discriminator Components

The EJB3 cartridge provides a few tagged values to customise the default values for the discriminator components for a single table inheritance mapping strategy.

The discriminator column name defaults to TYPE of type STRING. To set the discriminator column name, you model the andromda_persistence_discriminator_colum_name tagged value on the root class.

To specify the discriminator colum type, you model the andromda_persistence_discriminator_type tagged value on the root class. Your column type options are:

  • STRING
  • INTEGER
  • CHAR

You can set the discriminator column length if the colum type is specified as STRING by modeling the andromda_persistence_discriminator_column_length tagged value on the root class. The default is 10.

In some cases, you may want to explicitly define the SQL fragment when generating the DDL for the discriminator column. To do this, simply model the andromda_persistence_discriminator_colum_definition tagged value on the root class.

Most importantly, you can model the andromda_persistence_discriminator_value tagged value on all classes in the hierarchy. This value indicates the row in the table is an entity of the annotated entity type. This is shown in the above diagram on entities Vehicle and Car. However, by default, if no discriminator value is specified for single table mapping strategy, the cartridge will capitalize and assign the first letter of the entity as it's discriminator value.

Table Per Class Strategy

With this strategy, a table exists per class in the hierarchy and each table is comprised of all the properties persisted by that class. There is no need to define discriminator column types or values for this mapping strategy.

If the query is based on the root class in the hierarchy, the query is polymorphic which implies that entity subclass instances will be returned.

Cons: Poor support for polymorphic relationships. A separate SQL query per subclass is required before queries are executed.

Since the default inheritance mapping strategy is single table per hierarchy, you can model the andromda_persistence_inheritance tagged value and set it to TABLE_PER_CLASS on the root class of the hierarchy. All subclasses follow this same strategy.

Table Join Strategy

This strategy has a table per class in the hierarchy, however the subclass tables are comprised of ONLY the extra attributes defined in the subclass and not the inheriting fields. There is no need to define discriminator column types or values here. The primary key column(s) of the subclass table serves as a foreign key to the primary key of the superclass table.

If the query is based on the root class in the hierarchy, the query is polymorphic which implies that entity subclass instances will be returned.

Cons: Perhaps more than 1 join operation is needed to instantiate instances of a subclass.

Since the default inheritance mapping strategy is single table per hierarchy, you can model the andromda_persistence_inheritance tagged value and set it to JOINED on the root class of the hierarchy. All subclasses follow this same strategy.

Helpful Hints

The PFD EJB 3.0 spec says:

Support for the table per class inheritance mapping strategy is optional in this release.

Support for the combination of inheritance strategies within a single entity inheritance hierarchy is not required by this specification.

Next

To learn how to develop Message Driven Beans, click here.