Given a set of requirements and entity classes, choose and implement an appropriate inheritance hierarchy strategy and/or an appropriate mapping strategy.

[Note]

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.

Abstract Entity Classes

An abstract class CAN be specified as an entity. An abstract entity differs from a concrete entity only in that it CANNOT be directly instantiated. An abstract entity is mapped as an entity and can be the target of queries (which will operate over and/or retrieve instances of its concrete subclasses).

An abstract entity class is annotated with the Entity annotation or denoted in the XML descriptor as an entity.

The following example shows the use of an abstract entity class in the entity inheritance hierarchy:

@Entity
@Table(name="EMP")
@Inheritance(strategy=JOINED)
public abstract class Employee {

	@Id protected Integer empId;
	
	@Version protected Integer version;

	@ManyToOne protected Address address;

	...
}
					
@Entity
@Table(name="FT_EMP")
@DiscriminatorValue("FT")
@PrimaryKeyJoinColumn(name="FT_EMPID")
public class FullTimeEmployee extends Employee {
	
	// Inherit empId, but mapped in this class to FT_EMP.FT_EMPID
	
	// Inherit version mapped to EMP.VERSION
	
	// Inherit address mapped to EMP.ADDRESS fk
	
	// Defaults to FT_EMP.SALARY
	
	protected Integer salary;

	...
}
					
@Entity
@Table(name="PT_EMP")
@DiscriminatorValue("PT")
// PK field is PT_EMP.EMPID due to PrimaryKeyJoinColumn default
public class PartTimeEmployee extends Employee {

	protected Float hourlyWage;
	
	...
}
					

Mapped Superclasses

An entity may inherit from a superclass that provides persistent entity state and mapping information, but which is not itself an entity. Typically, the purpose of such a mapped superclass is to define state and mapping information that is common to multiple entity classes.

A mapped superclass, unlike an entity, is NOT queryable and CANNOT be passed as an argument to EntityManager or Query operations. A mapped superclass CANNOT be the target of a persistent relationship.

Both abstract and concrete classes may be specified as mapped superclasses. The MappedSuperclass annotation (or mapped-superclass XML descriptor element) is used to designate a mapped superclass.

A class designated as MappedSuperclass has NO separate table defined for it. Its mapping information is applied to the entities that inherit from it.

A class designated as MappedSuperclass can be mapped in the same way as an entity except that the mappings will apply only to its subclasses since no table exists for the mapped superclass itself. When applied to the subclasses, the inherited mappings will apply in the context of the subclass tables. Mapping information can be overridden in such subclasses by using the AttributeOverride and AssociationOverride annotations or corresponding XML elements.

All other entity mapping defaults apply equally to a class designated as MappedSuperclass.

The following example illustrates the definition of a concrete class as a mapped superclass:

@MappedSuperclass
public class Employee {

	@Id protected Integer empId;

	@Version protected Integer version;

	@ManyToOne @JoinColumn(name="ADDR")
	protected Address address;

	public Integer getEmpId() { ... }
	
	public void setEmpId(Integer id) { ... }
	
	public Address getAddress() { ... }
	
	public void setAddress(Address addr) { ... }
}
					
// Default table is FTEMPLOYEE table
@Entity
public class FTEmployee extends Employee {

	// Inherited empId field mapped to FTEMPLOYEE.EMPID
	
	// Inherited version field mapped to FTEMPLOYEE.VERSION

	// Inherited address field mapped to FTEMPLOYEE.ADDR fk

	// Defaults to FTEMPLOYEE.SALARY

	protected Integer salary;

	public FTEmployee() {}

	public Integer getSalary() { ... }

	public void setSalary(Integer salary) { ... }
}
					
@Entity @Table(name="PT_EMP")
@AssociationOverride(name="address", joincolumns=@JoinColumn(name="ADDR_ID"))
public class PartTimeEmployee extends Employee {

	// Inherited empId field mapped to PT_EMP.EMPID
	
	// Inherited version field mapped to PT_EMP.VERSION
	
	// address field mapping overridden to PT_EMP.ADDR_ID fk
	
	@Column(name="WAGE")
	protected Float hourlyWage;

	public PartTimeEmployee() {}

	public Float getHourlyWage() { ... }

	public void setHourlyWage(Float wage) { ... }
}					
					

Non-Entity Classes in the Entity Inheritance Hierarchy

An entity can have a non-entity superclass, which may be either a concrete or abstract class (the superclass may NOT be an embeddable class or id class).

The non-entity superclass serves for inheritance of behavior only. The state of a non-entity superclass is NOT PERSISTENT. Any state inherited from non-entity superclasses is non-persistent in an inheriting entity class. This non-persistent state is NOT managed by the EntityManager. Any annotations on such superclasses are IGNORED.

Non-entity classes CANNOT be passed as arguments to methods of the EntityManager or Query interfaces and CANNOT bear mapping information.

The following example illustrates the use of a non-entity class as a superclass of an entity:

public class Cart {

	// This state is transient
	Integer operationCount;

	public Cart() { operationCount = 0; }

	public Integer getOperationCount() { return operationCount; }

	public void incrementOperationCount() { operationCount++; }
}
					

@Entity
public class ShoppingCart extends Cart {

	Collection<Item> items = new Vector<Item>();

	public ShoppingCart() { super(); }

	...

	@OneToMany
	public Collection<Item> getItems() { return items; }

	public void addItem(Item item) {
		items.add(item);
		incrementOperationCount();
	}
}

					

Inheritance Mapping Strategies

The mapping of class hierarchies is specified through metadata.

There are three basic strategies that are used when mapping a class or class hierarchy to a relational database:

  1. Single Table per Class Hierarchy Strategy

    In this strategy, all the classes in a hierarchy are mapped to a single table. The table has a column that serves as a "discriminator column", that is, a column whose value identifies the specific subclass to which the instance that is represented by the row belongs.

    This mapping strategy provides good support for polymorphic relationships between entities and for queries that range over the class hierarchy.

    It has the drawback, however, that it requires that the columns that correspond to state specific to the subclasses be NULLABLE.

    Example of Single Table Mapping

    @Entity
    @Table(name="SUB", schema="CNTRCT")
    @Inheritance(strategy=InheritanceType.SINGLE_TABLE)
    public class Subscription {
    	...
    }
    								
    @Entity(name="Lifetime")
    public class LifetimeSubscription extends Subscription {
    	...
    }
    								

    The same metadata expressed in XML form:

    
    <entity class="Subscription">
    	<table name="SUB" schema="CNTRCT"/>
    	<inheritance strategy="SINGLE_TABLE"/>
    	...
    </entity>
    
    <entity class="LifetimeSubscription">
    	...
    </entity>
    
    								

    Single table inheritance is the default strategy. Thus, we could omit the @Inheritance annotation in the example above and get the same result.

    Advantages

    Single table inheritance mapping is the fastest of all inheritance models, since it never requires a join to retrieve a persistent instance from the database. Similarly, persisting or updating a persistent instance requires only a single INSERT or UPDATE statement. Finally, relations to any class within a single table inheritance hierarchy are just as efficient as relations to a base class.

    Disadvantages

    The larger the inheritance model gets, the "wider" the mapped table gets, in that for every field in the entire inheritance hierarchy, a column must exist in the mapped table. This may have undesirable consequence on the database size, since a wide or deep inheritance hierarchy will result in tables with many mostly-empty columns.

  2. Table per Concrete Class Strategy

    In this mapping strategy, each class is mapped to a separate table. All properties of the class, including inherited properties, are mapped to columns of the table for the class.

    This strategy has the following drawbacks:

    • It provides POOR support for polymorphic relationships.

    • It typically requires that SQL UNION queries (or a separate SQL query per subclass) be issued for queries that are intended to range over the class hierarchy.

    NOTE: Support for the table per concrete class inheritance mapping strategy is optional in EJB 3.0.

    Example of the Table Per Class Mapping:

    @Entity
    @Table(name="MAG")
    @Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
    public class Magazine {
    	...
    }
    								
    @Entity
    @Table(name="TABLOID")
    public class Tabloid extends Magazine {
    	...
    }
    								

    The same metadata expressed in XML form:

    
    <entity class="Magazine">
    	<table name="MAG"/>
    	<inheritance strategy="TABLE_PER_CLASS"/>
    	...
    </entity>
    
    <entity class="Tabloid">
    	<table name="TABLOID"/>
    	...
    </entity>
    
    								

    Advantages

    The table-per-class strategy is very efficient when operating on instances of a known class. Under these conditions, the strategy never requires joining to superclass or subclass tables. Reads, joins, inserts, updates, and deletes are all efficient in the absence of polymorphic behavior. Also, as in the joined strategy, adding additional classes to the hierarchy does not require modifying existing class tables.

    Disadvantages

    Polymorphic relations to non-leaf classes in a table-per-class hierarchy have many limitations. When the concrete subclass is not known, the related object could be in any of the subclass tables, making joins through the relation impossible. This ambiguity also affects identity lookups and queries; these operations require multiple SQL SELECTs (one for each possible subclass), or a complex UNION.

  3. Joined Subclass Strategy

    In the joined subclass strategy, the root of the class hierarchy is represented by a single table. Each subclass is represented by a separate table that contains those fields that are specific to the subclass (not inherited from its superclass), as well as the column(s) that represent its primary key. The primary key column(s) of the subclass table serves as a foreign key to the primary key of the superclass table.

    This strategy provides support for polymorphic relationships between entities.

    It has the drawback that it requires that one or more join operations be performed to instantiate instances of a subclass. In deep class hierarchies, this may lead to unacceptable performance. Queries that range over the class hierarchy likewise require joins.

    Example of Joined Subclass Tables:

    @Entity
    @Table(schema="CNTRCT")
    @Inheritance(strategy=InheritanceType.JOINED)
    public class Contract extends Document {
    	...
    }
    								
    @Entity
    @Table(name="LINE_ITEM", schema="CNTRCT")
    @PrimaryKeyJoinColumn(name="ID", referencedColumnName="PK")
    public class LineItem extends Contract {
    	...
    }
    								

    The same metadata expressed in XML form:

    
    <entity class="Contract">
    	<table schema="CNTRCT"/>
    	<inheritance strategy="JOINED"/>
    	...
    </entity>
    
    <entity class="LineItem">
    	<table name="LINE_ITEM" schema="CNTRCT"/>
    	<primary-key-join-column name="ID" referenced-column-name="PK"/>
    	...
    </entity>
    								
    								

    Advantages

    1. Using joined subclass tables results in the most normalized database schema, meaning the schema with the least spurious or redundant data.

    2. As more subclasses are added to the data model over time, the only schema modification that needs to be made is the addition of corresponding subclass tables in the database (rather than having to change the structure of existing tables).

    3. Relations to a base class using this strategy can be loaded through standard joins and can use standard foreign keys, as opposed to the machinations required to load polymorphic relations to table-per-class base types, described below.

    Disadvantages

    Aside from certain uses of the table-per-class strategy, the joined strategy is often the slowest of the inheritance models. Retrieving any subclass requires one or more database joins, and storing subclasses requires multiple INSERT or UPDATE statements. This is only the case when persistence operations are performed on subclasses; if most operations are performed on the least-derived persistent superclass, then this mapping is very fast.

Professional hosting     Belorussian informational portal         Free SCWCD 1.4 Study Guide     Free SCDJWS 1.4 Study Guide     SCDJWS 1.4 Quiz     Free IBM Certified Associate Developer Study Guide     IBM Test 000-287. Enterprise Application Development with IBM WebSphere Studio, V5.0 Study Guide     Free Mock Exam Engine