5.1. Mapping class inheritance

A simple strategy for mapping classes to database tables might be "one table for every entity persistent class." This approach sounds simple enough and, indeed, works well until we encounter inheritance.

Inheritance is such a visible structural mismatch between the object-oriented and relational worlds because object-oriented systems model both is a and has a relationships. SQL-based models provide only has a relationships between entities; SQL database management systems don't support type inheritance—and even when it's available, it's usually proprietary or incomplete.

There are four different approaches to representing an inheritance hierarchy:

  • Table per concrete class with implicit polymorphism—Use no explicit inheritance mapping, and default runtime polymorphic behavior.

  • Table per concrete class—Discard polymorphism and inheritance relationships completely from the SQL schema.

  • Table per class hierarchy—Enable polymorphism by denormalizing the SQL schema, and utilize a type discriminator column that holds type information.

  • Table per subclass—Represent is a (inheritance) relationships as has a (foreign key) relationships.

This section takes a top-down approach; it assumes that you're starting with a domain model and trying to derive a new SQL schema. However, the mapping strategies described are just as relevant if you're working bottom up, starting with existing database tables. We'll show some tricks along the way that help you dealing with nonperfect table layouts.

5.1.1. Table per concrete class with implicit polymorphism

Suppose we stick with the simplest approach suggested. You can use exactly one table for each (nonabstract) class. All properties of a class, including inherited properties, can be mapped to columns of this table, as shown in figure 5.1.

Figure 5-1. Mapping all concrete classes to an independent table

You don't have to do anything special in Hibernate to enable polymorphic behavior. The mapping for CreditCard and BankAccount is straightforward, each in its own entity <class> element, as we have done already for classes without a superclass (or persistent interfaces). Hibernate still knows about the superclass (or any interface) because it scans the persistent classes on startup.

The main problem with this approach is that it doesn't support polymorphic associations very well. In the database, associations are usually represented as foreign key relationships. In figure 5.1, if the subclasses are all mapped to different tables, a polymorphic association to their superclass (abstract BillingDetails in this example) can't be represented as a simple foreign key relationship. This would be problematic in our domain model, because BillingDetails is associated with User; both subclass tables would need a foreign key reference to the USERS table. Or, if User had a many-to-one relationship with BillingDetails, the USERS table would need a single foreign key column, which would have to refer both concrete subclass tables. This isn't possible with regular foreign key constraints.

Polymorphic queries (queries that return objects of all classes that match the interface of the queried class) are also problematic. A query against the superclass must be executed as several SQL SELECTs, one for each concrete subclass. For a query against the BillingDetails class Hibernate uses the following SQL:

select CREDIT_CARD_ID, OWNER, NUMBER, EXP_MONTH, EXP_YEAR ...
from CREDIT_CARD

select BANK_ACCOUNT_ID, OWNER, ACCOUNT, BANKNAME, ...
from BANK_ACCOUNT

Notice that a separate query is needed for each concrete subclass. On the other hand, queries against the concrete classes are trivial and perform well—only one of the statements is needed.

(Also note that here, and in other places in this book, we show SQL that is conceptually identical to the SQL executed by Hibernate. The actual SQL may look superficially different.)

A further conceptual problem with this mapping strategy is that several different columns, of different tables, share exactly the same semantics. This makes schema evolution more complex. For example, a change to a superclass property results in changes to multiple columns. It also makes it much more difficult to implement database integrity constraints that apply to all subclasses.

We recommend this approach (only) for the top level of your class hierarchy, where polymorphism isn't usually required, and when modification of the superclass in the future is unlikely.

Also, the Java Persistence interfaces don't support full polymorphic queries; only mapped entities (@Entity) can be officially part of a Java Persistence query (note that the Hibernate query interfaces are polymorphic, even if you map with annotations).

If you're relying on this implicit polymorphism, you map concrete classes with @Entity, as usual. However, you also have to duplicate the properties of the superclass to map them to all concrete class tables. By default, properties of the superclass are ignored and not persistent! You need to annotate the superclass to enable embedding of its properties in the concrete subclass tables:

@MappedSuperclass
public abstract class BillingDetails {

    @Column(name = "OWNER", nullable = false)
    private String owner;

       ...
}

Now map the concrete subclasses:

@Entity
@AttributeOverride(name = "owner", column =
  @Column(name = "CC_OWNER", nullable = false)
)
public class CreditCard extends BillingDetails {

    @Id @GeneratedValue
    @Column(name = "CREDIT_CARD_ID")
    private Long id = null;

    @Column(name = "NUMBER", nullable = false)
    private String number;

    ...
}

You can override column mappings from the superclass in a subclass with the @AttributeOverride annotation. You rename the OWNER column to CC_OWNER in the CREDIT_CARD table. The database identifier can also be declared in the superclass, with a shared column name and generator strategy for all subclasses.

Let's repeat the same mapping in a JPA XML descriptor:

<entity-mappings>

    <mapped-superclass class="auction.model.BillingDetails"
                       access="FIELD">
        <attributes>
            ...
        </attributes>
    </mapped-superclass>

    <entity class="auction.model.CreditCard" access="FIELD">
        <attribute-override name="owner">
            <column name="CC_OWNER" nullable="false"/>
        </attribute-override>
        <attributes>
            ...
        </attributes>
    </entity>
    ...
</entity-mappings>


Note:

A component is a value type; hence, the normal entity inheritance rules presented in this chapter don't apply. However, you can map a subclass as a component by including all the properties of the superclass (or interface) in your component mapping. With annotations, you use the @MappedSuperclass annotation on the superclass of the embeddable component you're mapping just like you would for an entity. Note that this feature is available only in Hibernate Annotations and isn't standardized or portable.


With the help of the SQL UNION operation, you can eliminate most of the issues with polymorphic queries and associations, which are present with this mapping strategy.

5.1.2. Table per concrete class with unions

First, let's consider a union subclass mapping with BillingDetails as an abstract class (or interface), as in the previous section. In this situation, we again have two tables and duplicate superclass columns in both: CREDIT_CARD and BANK_ACCOUNT. What's new is a special Hibernate mapping that includes the superclass, as you can see in listing 5.1.

Listing 5-1. Using the <union-subclass> inheritance strategy
<hibernate-mapping>
    <class
        name="BillingDetails"
        abstract="true"> 
<id
name="id" column="BILLING_DETAILS_ID" type="long"> <generator class="native"/> </id> <property
name="name" column="OWNER" type="string"/> ... <union-subclass
name="CreditCard" table="CREDIT_CARD"> <property name="number" column="NUMBER"/> <property name="expMonth" column="EXP_MONTH"/> <property name="expYear" column="EXP_YEAR"/> </union-subclass> <union-subclass name="BankAccount" table="BANK_ACCOUNT"> ... </class> </hibernate-mapping>

❶ An abstract superclass or an interface has to be declared as abstract="true"; otherwise a separate table for instances of the superclass is needed.

❷ The database identifier mapping is shared for all concrete classes in the hierarchy. The CREDIT_CARD and the BANK_ACCOUNT tables both have a BILLING_DETAILS_ID primary key column. The database identifier property now has to be shared for all subclasses; hence you have to move it into BillingDetails and remove it from CreditCard and BankAccount.

❸ Properties of the superclass (or interface) are declared here and inherited by all concrete class mappings. This avoids duplication of the same mapping.

❹ A concrete subclass is mapped to a table; the table inherits the superclass (or interface) identifier and other property mappings.

The first advantage you may notice with this strategy is the shared declaration of superclass (or interface) properties. No longer do you have to duplicate these mappings for all concrete classes—Hibernate takes care of this. Keep in mind that the SQL schema still isn't aware of the inheritance; effectively, we've mapped two unrelated tables to a more expressive class structure. Except for the different primary key column name, the tables look exactly alike, as shown in figure 5.1.

In JPA annotations, this strategy is known as TABLE_PER_CLASS:

@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class BillingDetails {

    @Id @GeneratedValue
    @Column(name = "BILLING_DETAILS_ID")
    private Long id = null;

    @Column(name = "OWNER", nullable = false)
    private String owner;

    ...
}

The database identifier and its mapping have to be present in the superclass, to be shared across all subclasses and their tables. An @Entity annotation on each subclass is all that is required:

@Entity
@Table(name = "CREDIT_CARD")
public class CreditCard extends BillingDetails {

    @Column(name = "NUMBER", nullable = false)
    private String number;
    ...
}

Note that TABLE_PER_CLASS is specified in the JPA standard as optional, so not all JPA implementations may support it. The actual implementation is also vendor dependent—in Hibernate, it's equivalent to a <union-subclass> mapping in XML files.

The same mapping looks like this in a JPA XML descriptor:

<entity-mappings>

    <entity class="auction.model.BillingDetails" access="FIELD">
        <inheritance strategy="TABLE_PER_CLASS"/>
        ...
    </entity>

    <entity class="auction.model.CreditCard" access="FIELD"/>

    <entity class="auction.model.BankAccount" access="FIELD"/>

</entity-mappings>

If your superclass is concrete, then an additional table is needed to hold instances of that class. We have to emphasize again that there is still no relationship between the database tables, except for the fact that they share some similar columns. The advantages of this mapping strategy are clearer if we examine polymorphic queries. For example, a query for BillingDetails executes the following SQL statement:

select
    BILLING_DETAILS_ID, OWNER,
    NUMBER, EXP_MONTH, EXP_YEAR,
    ACCOUNT, BANKNAME, SWIFT
    CLAZZ_
from
  ( select
      BILLING_DETAILS_ID, OWNER,
      NUMBER, EXP_MONTH, EXP_YEAR,
      null as ACCOUNT, null as BANKNAME, null as SWIFT,
      1 as CLAZZ_
    from
      CREDIT_CARD

    union

    select
        BILLING_DETAILS_ID, OWNER,
        null as NUMBER, null as EXP_MONTH, null as EXP_YEAR, ...
        ACCOUNT, BANKNAME, SWIFT,
        2 as CLAZZ_
      from
        BANK_ACCOUNT
   )

This SELECT uses a FROM-clause subquery to retrieve all instances of BillingDetails from all concrete class tables. The tables are combined with a UNION operator, and a literal (in this case, 1 and 2) is inserted into the intermediate result; Hibernate reads this to instantiate the correct class given the data from a particular row. A union requires that the queries that are combined project over the same columns; hence, we have to pad and fill up nonexistent columns with NULL. You may ask whether this query will really perform better than two separate statements. Here we can let the database optimizer find the best execution plan to combine rows from several tables, instead of merging two result sets in memory as Hibernate's polymorphic loader engine would do.

Another much more important advantage is the ability to handle polymorphic associations; for example, an association mapping from User to BillingDetails would now be possible. Hibernate can use a UNION query to simulate a single table as the target of the association mapping. We cover this topic in detail in chapter 7, section 7.3, "Polymorphic associations."

So far, the inheritance mapping strategies we've discussed don't require extra consideration with regard to the SQL schema. No foreign keys are needed, and relations are properly normalized. This situation changes with the next strategy.

5.1.3. Table per class hierarchy

An entire class hierarchy can be mapped to a single table. This table includes columns for all properties of all classes in the hierarchy. The concrete subclass represented by a particular row is identified by the value of a type discriminator column. This approach is shown in figure 5.2.

This mapping strategy is a winner in terms of both performance and simplicity. It's the best-performing way to represent polymorphism—both polymorphic and nonpolymorphic queries perform well—and it's even easy to implement by hand. Ad-hoc reporting is possible without complex joins or unions. Schema evolution is straightforward.

Figure 5-2. Mapping a whole class hierarchy to a single table

There is one major problem: Columns for properties declared by subclasses must be declared to be nullable. If your subclasses each define several nonnullable properties, the loss of NOT NULL constraints may be a serious problem from the point of view of data integrity. Another important issue is normalization. We've created functional dependencies between nonkey columns, violating the third normal form. As always, denormalization for performance can be misleading, because it sacrifices long-term stability, maintainability, and the integrity of data for immediate gains that may be also achieved by proper optimization of the SQL execution plans (in other words, ask your DBA).

In Hibernate, you use the <subclass> element to create a table per class hierarchy mapping, as in listing 5.2.

Listing 5-2. Hibernate <subclass> mapping
<hibernate-mapping>
    <class 
name="BillingDetails" table="BILLING_DETAILS"> <id name="id" column="BILLING_DETAILS_ID" type="long"> <generator class="native"/> </id> <discriminator
column="BILLING_DETAILS_TYPE" type="string"/> <property
name="owner" column="OWNER" type="string"/> ... <subclass
name="CreditCard" discriminator-value="CC"> <property name="number" column="CC_NUMBER"/> <property name="expMonth" column="CC_EXP_MONTH"/> <property name="expYear" column="CC_EXP_YEAR"/> </subclass> <subclass name="BankAccount" discriminator-value="BA"> ... </class> </hibernate-mapping>

❶ The root class BillingDetails of the inheritance hierarchy is mapped to the table BILLING_DETAILS.

❷ You have to add a special column to distinguish between persistent classes: the discriminator. This isn't a property of the persistent class; it's used internally by Hibernate. The column name is BILLING_DETAILS_TYPE, and the values are strings—in this case, "CC" or "BA". Hibernate automatically sets and retrieves the discriminator values.

❸ Properties of the superclass are mapped as always, with a simple <property> element.

❹ Every subclass has its own <subclass> element. Properties of a subclass are mapped to columns in the BILLING_DETAILS table. Remember that NOT NULL constraints aren't allowed, because a BankAccount instance won't have an expMonth property, and the CC_EXP_MONTH field must be NULL for that row.

The <subclass> element can in turn contain other nested <subclass> elements, until the whole hierarchy is mapped to the table.

Hibernate generates the following SQL when querying the BillingDetails class:

select
    BILLING_DETAILS_ID, BILLING_DETAILS_TYPE, OWNER,
    CC_NUMBER, CC_EXP_MONTH, ..., BA_ACCOUNT, BA_BANKNAME, ...
from BILLING_DETAILS

To query the CreditCard subclass, Hibernate adds a restriction on the discriminator column:

select BILLING_DETAILS_ID, OWNER, CC_NUMBER, CC_EXP_MONTH, ...
from BILLING_DETAILS
where BILLING_DETAILS_TYPE='CC'

This mapping strategy is also available in JPA, as SINGLE_TABLE:

@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(
    name = "BILLING_DETAILS_TYPE",
    discriminatorType = DiscriminatorType.STRING
)

public abstract class BillingDetails {

    @Id @GeneratedValue
    @Column(name = "BILLING_DETAILS_ID")
    private Long id = null;

    @Column(name = "OWNER", nullable = false)
    private String owner;

    ...
}

If you don't specify a discriminator column in the superclass, its name defaults to DTYPE and its type to string. All concrete classes in the inheritance hierarchy can have a discriminator value; in this case, BillingDetails is abstract, and CreditCard is a concrete class:

@Entity
@DiscriminatorValue("CC")
public class CreditCard extends BillingDetails {

    @Column(name = "CC_NUMBER")
    private String number;

    ...
}

Without an explicit discriminator value, Hibernate defaults to the fully qualified class name if you use Hibernate XML files and the entity name if you use annotations or JPA XML files. Note that no default is specified in Java Persistence for non-string discriminator types; each persistence provider can have different defaults.

This is the equivalent mapping in JPA XML descriptors:

<entity-mappings>
    <entity class="auction.model.BillingDetails" access="FIELD">
        <inheritance strategy="SINGLE_TABLE"/>
        <discriminator-column name="BILLING_DETAILS_TYPE"
            discriminator-type="STRING"/>
        ...

    </entity>

    <entity class="auction.model.CreditCard" access="FIELD">
        <discriminator-value>CC</discriminator-value>

        ...
    </entity>

</entity-mappings>

Sometimes, especially in legacy schemas, you don't have the freedom to include an extra discriminator column in your entity tables. In this case, you can apply a formula to calculate a discriminator value for each row:

<discriminator
  formula="case when CC_NUMBER is not null then 'CC' else 'BA' end"
  type="string"/>

    ...

<subclass
    name="CreditCard"
    discriminator-value="CC">

   ...

This mapping relies on an SQL CASE/WHEN expression to determine whether a particular row represents a credit card or a bank account (many developers never used this kind of SQL expression; check the ANSI standard if you aren't familiar with it). The result of the expression is a literal, CC or BA, which in turn is declared on the <subclass> mappings. Formulas for discrimination aren't part of the JPA specification. However, you can apply a Hibernate annotation:

@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@org.hibernate.annotations.DiscriminatorFormula(
    "case when CC_NUMBER is not null then 'CC' else 'BA' end"
)
public abstract class BillingDetails {
    ...
}

The disadvantages of the table per class hierarchy strategy may be too serious for your design—after all, denormalized schemas can become a major burden in the long run. Your DBA may not like it at all. The next inheritance mapping strategy doesn't expose you to this problem.

5.1.4. Table per subclass

The fourth option is to represent inheritance relationships as relational foreign key associations. Every class/subclass that declares persistent properties—including abstract classes and even interfaces—has its own table.

Unlike the table per concrete class strategy we mapped first, the table here contains columns only for each noninherited property (each property declared by the subclass itself) along with a primary key that is also a foreign key of the superclass table. This approach is shown in figure 5.3.

If an instance of the CreditCard subclass is made persistent, the values of properties declared by the BillingDetails superclass are persisted to a new row of the BILLING_DETAILS table. Only the values of properties declared by the subclass are persisted to a new row of the CREDIT_CARD table. The two rows are linked together by their shared primary key value. Later, the subclass instance may be retrieved from the database by joining the subclass table with the superclass table.

Figure 5-3. Mapping all classes of the hierarchy to their own table

The primary advantage of this strategy is that the SQL schema is normalized. Schema evolution and integrity constraint definition are straightforward. A polymorphic association to a particular subclass may be represented as a foreign key referencing the table of that particular subclass.

In Hibernate, you use the <joined-subclass> element to create a table per subclass mapping. See listing 5.3.

Listing 5-3. Hibernate <joined-subclass> mapping
<hibernate-mapping>
    <class 
name="BillingDetails" table="BILLING_DETAILS"> <id name="id" column="BILLING_DETAILS_ID" type="long"> <generator class="native"/> </id> <property name="owner" column="OWNER" type="string"/> ... <joined-subclass
name="CreditCard" table="CREDIT_CARD"> <key column="CREDIT_CARD_ID"/>
<property name="number" column="NUMBER"/> <property name="expMonth" column="EXP_MONTH"/> <property name="expYear" column="EXP_YEAR"/> </joined-subclass> <joined-subclass name="BankAccount" table="BANK_ACCOUNT"> ... </class> </hibernate-mapping>

❶ The root class BillingDetails is mapped to the table BILLING_DETAILS. Note that no discriminator is required with this strategy.

❷ The new <joined-subclass> element maps a subclass to a new table—in this example, CREDIT_CARD. All properties declared in the joined subclass are mapped to this table.

❸ A primary key is required for the CREDIT_CARD table. This column also has a foreign key constraint to the primary key of the BILLING_DETAILS table. A CreditCard object lookup requires a join of both tables. A <joined-subclass> element may contain other nested <joined-subclass> elements, until the whole hierarchy has been mapped.

Hibernate relies on an outer join when querying the BillingDetails class:

select BD.BILLING_DETAILS_ID, BD.OWNER,
       CC.NUMBER, CC.EXP_MONTH, ..., BA.ACCOUNT, BA.BANKNAME, ...
    case
        when CC.CREDIT_CARD_ID is not null then 1
        when BA.BANK_ACCOUNT_ID is not null then 2
        when BD.BILLING_DETAILS_ID is not null then 0
    end as CLAZZ_

from BILLING_DETAILS BD
    left join CREDIT_CARD CC
      on BD.BILLING_DETAILS_ID = CC.CREDIT_CARD_ID
    left join BANK_ACCOUNT BA
      on BD.BILLING_DETAILS_ID = BA.BANK_ACCOUNT_ID

The SQL CASE statement detects the existence (or absence) of rows in the subclass tables CREDIT_CARD and BANK_ACCOUNT, so Hibernate can determine the concrete subclass for a particular row of the BILLING_DETAILS table.

To narrow the query to the subclass, Hibernate uses an inner join:

select BD.BILLING_DETAILS_ID, BD.OWNER, CC.NUMBER, ...
from CREDIT_CARD CC
    inner join BILLING_DETAILS BD
      on BD.BILLING_DETAILS_ID = CC.CREDIT_CARD_ID

As you can see, this mapping strategy is more difficult to implement by hand—even ad-hoc reporting is more complex. This is an important consideration if you plan to mix Hibernate code with handwritten SQL.

Furthermore, even though this mapping strategy is deceptively simple, our experience is that performance can be unacceptable for complex class hierarchies. Queries always require either a join across many tables or many sequential reads.

Let's map the hierarchy with the same strategy and annotations, here called the JOINED strategy:

@Entity
@Inheritance(strategy = InheritanceType.JOINED)
public abstract class BillingDetails {

    @Id @GeneratedValue
    @Column(name = "BILLING_DETAILS_ID")
    private Long id = null;

    ...
}

In subclasses, you don't need to specify the join column if the primary key column of the subclass table has (or is supposed to have) the same name as the primary key column of the superclass table:

@Entity
public class BankAccount {
    ...
}

This entity has no identifier property; it automatically inherits the BILLING_DETAILS_ID property and column from the superclass, and Hibernate knows how to join the tables together if you want to retrieve instances of BankAccount. Of course, you can specify the column name explicitly:

@Entity
@PrimaryKeyJoinColumn(name = "CREDIT_CARD_ID")
public class CreditCard {
    ...
}

Finally, this is the equivalent mapping in JPA XML descriptors:

<entity-mappings>

    <entity class="auction.model.BillingDetails" access="FIELD">
        <inheritance strategy="JOINED"/>
        ...
    </entity>

    <entity class="auction.model.BankAccount" access="FIELD"/>
    <entity class="auction.model.CreditCard" access="FIELD">
        <primary-key-join-column name="CREDIT_CARD_ID"/>
    </entity>

</entity-mappings>

Before we show you when to choose which strategy, let's consider mixing inheritance mapping strategies in a single class hierarchy.

5.1.5. Mixing inheritance strategies

You can map whole inheritance hierarchies by nesting <union-subclass>, <subclass>, and <joined-subclass> mapping elements. You can't mix them—for example, to switch from a table-per-class hierarchy with a discriminator to a normalized table-per-subclass strategy. Once you've made a decision for an inheritance strategy, you have to stick to it.

This isn't completely true, however. With some Hibernate tricks, you can switch the mapping strategy for a particular subclass. For example, you can map a class hierarchy to a single table, but for a particular subclass, switch to a separate table with a foreign key mapping strategy, just as with table per subclass. This is possible with the <join> mapping element:

<hibernate-mapping>
<class name="BillingDetails"
      table="BILLING_DETAILS">

    <id>...</id>

    <discriminator
        column="BILLING_DETAILS_TYPE"
        type="string"/>

        ...
    <subclass
        name="CreditCard"
        discriminator-value="CC">

        <join table="CREDIT_CARD">
            <key column="CREDIT_CARD_ID"/>

            <property name="number" column="CC_NUMBER"/>
            <property name="expMonth" column="CC_EXP_MONTH"/>
            <property name="expYear" column="CC_EXP_YEAR"/>
            ...
        </join>

    </subclass>

    <subclass
        name="BankAccount"
        discriminator-value="BA">

        <property name=account" column="BA_ACCOUNT"/>
        ...
    </subclass>

...

</class>
</hibernate-mapping>

The <join> element groups some properties and tells Hibernate to get them from a secondary table. This mapping element has many uses, and you'll see it again later in the book. In this example, it separates the CreditCard properties from the table per hierarchy into the CREDIT_CARD table. The CREDIT_CARD_ID column of this table is at the same time the primary key, and it has a foreign key constraint referencing the BILLING_DETAILS_ID of the hierarchy table. The BankAccount subclass is mapped to the hierarchy table. Look at the schema in figure 5.4.

At runtime, Hibernate executes an outer join to fetch BillingDetails and all subclass instances polymorphically:

select
    BILLING_DETAILS_ID, BILLING_DETAILS_TYPE, OWNER,
    CC.CC_NUMBER, CC.CC_EXP_MONTH, CC.CC_EXP_YEAR,
    BA_ACCOUNT, BA_BANKNAME, BA_SWIFT

from
    BILLING_DETAILS
left outer join
    CREDIT_CARD CC
        on BILLING_DETAILS_ID = CC.CREDIT_CARD_ID

Figure 5-4. Breaking out a subclass to its own secondary table

You can also use the <join> trick for other subclasses in your class hierarchy. However, if you have an exceptionally wide class hierarchy, the outer join can become a problem. Some database systems (Oracle, for example) limit the number of tables in an outer join operation. For a wide hierarchy, you may want to switch to a different fetching strategy that executes an immediate second select instead of an outer join:

<subclass
    name="CreditCard"
    discriminator-value="CC">

    <join table="CREDIT_CARD" fetch="select">
        <key column="CREDIT_CARD_ID"/>
        ...
    </join>

</subclass>

Java Persistence also supports this mixed inheritance mapping strategy with annotations. Map the superclass BillingDetails with InheritanceType.SINGLE_TABLE, as you did before. Now map the subclass you want to break out of the single table to a secondary table.

@Entity
@DiscriminatorValue("CC")
@SecondaryTable(

  name = "CREDIT_CARD",
  pkJoinColumns =  @PrimaryKeyJoinColumn(name = "CREDIT_CARD_ID")
)
public class CreditCard extends BillingDetails {

    @Column(table = "CREDIT_CARD",
            name = "CC_NUMBER",
            nullable = false)
    private String number;
    ...
}

If you don't specify a primary key join column for the secondary table, the name of the primary key of the single inheritance table is used—in this case, BILLING_DETAILS_ID. Also note that you need to map all properties that are moved into the secondary table with the name of that secondary table.

You also want more tips about how to choose an appropriate combination of mapping strategies for your application's class hierarchies.

5.1.6. Choosing a strategy

You can apply all mapping strategies to abstract classes and interfaces. Interfaces may have no state but may contain accessor method declarations, so they can be treated like abstract classes. You can map an interface with <class>, <union-subclass>, <subclass>, or <joined-subclass>, and you can map any declared or inherited property with <property>. Hibernate won't try to instantiate an abstract class, even if you query or load it.


Note:

Note that the JPA specification doesn't support any mapping annotation on an interface! This will be resolved in a future version of the specification; when you read this book, it will probably be possible with Hibernate Annotations.


Here are some rules of thumb:

  • If you don't require polymorphic associations or queries, lean toward table-per-concrete-class—in other words, if you never or rarely query for BillingDetails and you have no class that has an association to BillingDetails (our model has). An explicit UNION-based mapping should be preferred, because (optimized) polymorphic queries and associations will then be possible later. Implicit polymorphism is mostly useful for queries utilizing non-persistence-related interfaces.

  • If you do require polymorphic associations (an association to a superclass, hence to all classes in the hierarchy with dynamic resolution of the concrete class at runtime) or queries, and subclasses declare relatively few properties (particularly if the main difference between subclasses is in their behavior), lean toward table-per-class-hierarchy. Your goal is to minimize the number of nullable columns and to convince yourself (and your DBA) that a denormalized schema won't create problems in the long run.

  • If you do require polymorphic associations or queries, and subclasses declare many properties (subclasses differ mainly by the data they hold), lean toward table-per-subclass. Or, depending on the width and depth of your inheritance hierarchy and the possible cost of joins versus unions, use table-per-concrete-class.

By default, choose table-per-class-hierarchy only for simple problems. For more complex cases (or when you're overruled by a data modeler insisting on the importance of nullability constraints and normalization), you should consider the table-per-subclass strategy. But at that point, ask yourself whether it may not be better to remodel inheritance as delegation in the object model. Complex inheritance is often best avoided for all sorts of reasons unrelated to persistence or ORM. Hibernate acts as a buffer between the domain and relational models, but that doesn't mean you can ignore persistence concerns when designing your classes.

When you start thinking about mixing inheritance strategies, remember that implicit polymorphism in Hibernate is smart enough to handle more exotic cases. For example, consider an additional interface in our application, ElectronicPaymentOption. This is a business interface that doesn't have a persistence aspect—except that in our application, a persistent class such as CreditCard will likely implement this interface. No matter how you map the BillingDetails hierarchy, Hibernate can answer a query from ElectronicPaymentOption correctly. This even works if other classes, which aren't part of the BillingDetails hierarchy, are mapped persistent and implement this interface. Hibernate always know what tables to query, which instances to construct, and how to return a polymorphic result.

Finally, you can also use <union-subclass>, <subclass>, and <joined-subclass> mapping elements in a separate mapping file (as a top-level element instead of <class>). You then have to declare the class that is extended, such as <subclass name="CreditCard" extends="BillingDetails">, and the superclass mapping must be loaded programmatically before the subclass mapping file (you don't have to worry about this order when you list mapping resources in the XML configuration file). This technique allows you to extend a class hierarchy without modifying the mapping file of the superclass.

You now know everything you need to know about the mapping of entities, properties, and inheritance hierarchies. You can already map complex domain models. In the second half of this chapter, we discuss another important feature that you should know by heart as a Hibernate user: the Hibernate mapping type system.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.138.134.188