Table per Subclass
In the table per subclass mapping, each subclass has its own table, but this table contains only the properties that are defined on that particular class. In other words, it is similar to the TABLE_PER_CLASS
strategy, except the schema is normalized. This is also called the JOINED
strategy.
create table Person (
id integer primary key not null,
firstName varchar(255),
lastName varchar(255),
);
create table Customer (
id integer primary key not null,
street varchar(255),
city varchar(255),
state varchar(255),
zip varchar(255),
);
create table Employee (
EMP_PK integer primary key not null,
employeeId integer
);
When the persistence manager loads an entity that is a subclass or traverses a polymorphic relationship, it does an SQL join on all the tables in the hierarchy. In this mapping, there must be a column in each table that can be used to join each table. In our example, the EMPLOYEE, CUSTOMER, and PERSON tables share the same primary-key values. The annotation mapping is quite simple:
@Entity
@Inheritance(strategy=InheritanceType.JOINED)
public class Person {
...
}
@Entity
public class Customer extends Person {
...
}
@Entity
@PrimaryKeyJoinColumn
(name="EMP_PK")
public class Employee extends Customer {
...
}
The persistence manager needs to know which columns in each table will be used to perform a join when loading an entity with a JOINED inheritance strategy. The @javax.persistence.PrimaryKeyJoinColumn
annotation can be used to describe this metadata:
package javax.persistence;
@Target({TYPE, METHOD, FIELD})
public @interface PrimaryKeyJoinColumn
String name( ) default "";
String referencedColumnName( ) default "";
String columnDefinition( ) default "";
}
The name( ) attribute refers to the column you will perform a join on that is contained in the current table. It defaults to the primary-key column of the superclass's table. The referencedColumnName( ) is the column that will be used to perform the join from the superclass's table. It can be any column in the superclass's table, but it defaults to its primary key. If the primary-key column names are identical between the base and subclasses, then this annotation is not needed. For instance, the Customer entity does not need the @PrimaryKeyJoinColumn annotation. The Employee class has a different primary-key column name than the tables of its superclasses, so the @PrimaryKeyJoinColumn annotation is required. If class hierarchy uses a composite key, there is a @javax.persistence.PrimaryKeyJoinColumns
annotation that can describe multiple join columns:
package javax.persistence;
@Target({TYPE, METHOD, FIELD})
public @interface PrimaryKeyJoinColumns {
@PrimaryKeyJoinColumns[] value( );
}
 |
Some persistence providers require a discriminator column for this mapping type. Most do not. Make sure to check your persistence provider implementation to see whether it is required.
|
|
Let's look at the XML mapping for this strategy type:
<entity-mappings>
<entity class="com.titan.domain.Person">
<inheritance strategy="JOINED"/>
<attributes>
<id>
<generated-value/>
</id>
</attributes>
</entity>
<entity class="com.titan.domain.Customer"/>
<entity class="com.titan.domain.Employee">
<primary-key-join-column name="EMP_PK"/>
</entity>
</entity-mappings>
The <inheritance> element is used to define that the Person class is part of an inheritance hierarchy mapped by the JOINED strategy. Then, for Employee, we need to define that its primary-key name is different from Person's primary-key column.
Advantages
It is better to compare this mapping to other strategies to describe its advantages.
Although it is not as fast as the SINGLE_TABLE
strategy, you are able to define NOT NULL
constraints on any column of any table and your model is normalized.
This mapping is better than the TABLE_PER_CLASS
strategy for two reasons. One, the relational database model is completely normalized. Two, it performs better than the TABLE_PER_CLASS strategy if SQL UNION
s are not supported.
Disadvantages
It does not perform as well as the SINGLE_TABLE strategy.
 |