6. Inheritance
Prev | Chapter 13. Mapping Metadata | Next

## 6. Inheritance

In the 1990's programmers coined the term impedance mismatch to describe the difficulties in bridging the object and relational worlds. Perhaps no feature of object modeling highlights the impedance mismatch better than inheritance. There is no natural, efficient way to represent an inheritance relationship in a relational database.

Luckily, JPA gives you a choice of inheritance strategies, making the best of a bad situation. The base entity class defines the inheritance strategy for the hierarchy with the `Inheritance` annotation. `Inheritance` has the following properties:

- `InheritanceType strategy`: Enum value declaring the inheritance strategy for the hierarchy. Defaults to `InheritanceType.SINGLE_TABLE`. We detail each of the available strategies below.

The corresponding XML element is `inheritance`, which has a single attribute:

- `strategy`: One of `SINGLE_TABLE`, `JOINED`, or `TABLE_PER_CLASS`.

The following sections describe JPA's standard inheritance strategies.

### Note

OpenJPA allows you to vary your inheritance strategy for each class, rather than forcing a single strategy per inheritance hierarchy. See Section 7, “ Additional JPA Mappings ” in the Reference Guide for details.

### 6.1. Single Table

The `InheritanceType.SINGLE_TABLE` strategy maps all classes in the hierarchy to the base class' table.

In our model, `Subscription` is mapped to the `CNTRCT.SUB` table. `LifetimeSubscription`, which extends `Subscription`, adds its field data to this table as well.

Example 13.5. 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="org.mag.subcribe.Subscription">
        <table name="SUB" schema="CNTRCT"/>
        <inheritance strategy="SINGLE_TABLE"/>
        ...
    </entity>
    <entity class="org.mag.subscribe.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.

### Note

Mapping subclass state to the superclass table is often called flat inheritance mapping.

#### 6.1.1. 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.

#### 6.1.2. 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.

### 6.2. Joined

The `InheritanceType.JOINED` strategy uses a different table for each class in the hierarchy. Each table only includes state declared in its class. Thus to load a subclass instance, the JPA implementation must read from the subclass table as well as the table of each ancestor class, up to the base entity class.

### Note

Using joined subclass tables is also called vertical inheritance mapping.

`PrimaryKeyJoinColumn` annotations tell the JPA implementation how to join each subclass table record to the corresponding record in its direct superclass table. In our model, the `LINE_ITEM.ID` column joins to the `CONTRACT.ID` column. The `PrimaryKeyJoinColumn` annotation has the following properties:

- `String name`: The name of the subclass table column. When there is a single identity field, defaults to that field's column name.

- `String referencedColumnName`: The name of the superclass table column this subclass table column joins to. When there is a single identity field, defaults to that field's column name.

- `String columnDefinition`: This property has the same meaning as the `columnDefinition` property on the `Column` annotation, described in Section 3, “ Column ”.

The XML equivalent is the `primary-key-join-column` element. Its attributes mirror the annotation properties described above:

- `name`

- `referenced-column-name`

- `column-definition`

The example below shows how we use `InheritanceTable.JOINED` and a primary key join column to map our sample model according to the diagram above. Note that a primary key join column is not strictly needed, because there is only one identity column, and the subclass table column has the same name as the superclass table column. In this situation, the defaults suffice. However, we include the primary key join column for illustrative purposes.

Example 13.6. Joined Subclass Tables

    @Entity
    @Table(schema="CNTRCT")
    @Inheritance(strategy=InheritanceType.JOINED)
    public class Contract
        extends Document {
        ...
    }

    public class Subscription {
        ...

        @Entity
        @Table(name="LINE_ITEM", schema="CNTRCT")
        @PrimaryKeyJoinColumn(name="ID", referencedColumnName="ID")
        public static class LineItem
            extends Contract {
            ...
        }
    }

The same metadata expressed in XML form:

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

When there are multiple identity columns, you must define multiple `PrimaryKeyJoinColumn`s using the aptly-named `PrimaryKeyJoinColumns` annotation. This annotation's value is an array of `PrimaryKeyJoinColumn` s. We could rewrite `LineItem`'s mapping as:

    @Entity
    @Table(name="LINE_ITEM", schema="CNTRCT")
    @PrimaryKeyJoinColumns({
        @PrimaryKeyJoinColumn(name="ID", referencedColumnName="ID")
    })
    public static class LineItem
        extends Contract {
        ...
    }

In XML, simply list as many `primary-key-join-column` elements as necessary.

#### 6.2.1. Advantages

The joined strategy has the following 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.

#### 6.2.2. Disadvantages

Aside from certain uses of the table-per-class strategy described below, 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.

### Note

When executing a select against a hierarchy that uses joined subclass table inheritance, you must consider how to load subclass state. Section 8, “ Eager Fetching ” in the Reference Guide describes OpenJPA's options for efficient data loading.

### 6.3. Table Per Class

Like the `JOINED` strategy, the `InheritanceType.TABLE_PER_CLASS` strategy uses a different table for each class in the hierarchy. Unlike the `JOINED` strategy, however, each table includes all state for an instance of the corresponding class. Thus to load a subclass instance, the JPA implementation must only read from the subclass table; it does not need to join to superclass tables.

Suppose that our sample model's `Magazine` class has a subclass `Tabloid`. The classes are mapped using the table-per-class strategy, as in the diagram above. In a table-per-class mapping, `Magazine`'s table `MAG` contains all state declared in the base `Magazine` class. `Tabloid` maps to a separate table, `TABLOID`. This table contains not only the state declared in the `Tabloid` subclass, but all the base class state from `Magazine` as well. Thus the `TABLOID` table would contain columns for `isbn`, `title`, and other `Magazine` fields. These columns would default to the names used in `Magazine`'s mapping metadata. Section 8.3, “ Embedded Mapping ” will show you how to use `AttributeOverride`s and `AssociationOverride` s to override superclass field mappings.

Example 13.7. 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 {
        ...
    }

And the same classes in XML:

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

#### 6.3.1. 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.

#### 6.3.2. 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 `SELECT`s (one for each possible subclass), or a complex `UNION`.

### Note

Section 8.1, “ Table Per Class ” in the Reference Guide describes the limitations OpenJPA places on table-per-class mapping.

### 6.4. Putting it All Together

Now that we have covered JPA's inheritance strategies, we can update our mapping document with inheritance information. Here is the complete model:

And here is the corresponding mapping metadata:

Example 13.8. Inheritance Mapping

    package org.mag;

    @Entity
    @IdClass(Magazine.MagazineId.class)
    @Table(name="MAG")
    public class Magazine {

        @Column(length=9)
        @Id private String isbn;
        @Id private String title;

        ...

        public static class MagazineId {
            ...
        }
    }

    @Entity
    @Table(name="ART", uniqueConstraints=@Unique(columnNames="TITLE"))
    @SequenceGenerator(name="ArticleSeq", sequenceName="ART_SEQ")
    public class Article {

        @Id
        @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="ArticleSeq")
        private long id;

        ...
    }

    package org.mag.pub;

    @Entity
    @Table(name="COMP")
    public class Company {

        @Column(name="CID")
        @Id private long id;

        ...
    }

    @Entity
    @Table(name="AUTH")
    public class Author {

        @Id
        @GeneratedValue(strategy=GenerationType.TABLE, generator="AuthorGen")
        @TableGenerator(name="AuthorGen", table="AUTH_GEN", pkColumnName="PK",
            valueColumnName="AID")
        @Column(name="AID", columnDefinition="INTEGER64")
        private long id;

        ...
    }

    @Embeddable
    public class Address {
        ...
    }

    package org.mag.subscribe;

    @MappedSuperclass
    public abstract class Document {

        @Id
        @GeneratedValue(strategy=GenerationType.IDENTITY)
        private long id;

        ...
    }

    @Entity
    @Table(schema="CNTRCT")
    @Inheritance(strategy=InheritanceType.JOINED)
    public class Contract
        extends Document {
        ...
    }

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

        @Id
        @GeneratedValue(strategy=GenerationType.IDENTITY)
        private long id;

        ...

        @Entity
        @Table(name="LINE_ITEM", schema="CNTRCT")
        @PrimaryKeyJoinColumn(name="ID", referencedColumnName="ID")
        public static class LineItem
            extends Contract {
            ...
        }
    }

    @Entity(name="Lifetime")
    public class LifetimeSubscription
        extends Subscription {
        ...
    }

    @Entity(name="Trial")
    public class TrialSubscription
        extends Subscription {
        ...
    }

The same metadata expressed in XML form:

    <entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm orm_1_0.xsd"
        version="1.0">
        <mapped-superclass class="org.mag.subscribe.Document">
            <attributes>
                <id name="id">
                    <generated-value strategy="IDENTITY"/>
                </id>
                ...
            </attributes>
        </mapped-superclass>
        <entity class="org.mag.Magazine">
            <table name="MAG"/>
            <id-class="org.mag.Magazine.MagazineId"/>
            <attributes>
                <id name="isbn">
                    <column length="9"/>
                </id>
                <id name="title"/>
                ...
            </attributes>
        </entity>
        <entity class="org.mag.Article">
            <table name="ART">
                <unique-constraint>
                   <column-name>TITLE</column-name>
                </unique-constraint>
            </table>
            <sequence-generator name="ArticleSeq" sequence-name="ART_SEQ"/>
            <attributes>
                <id name="id">
                    <generated-value strategy="SEQUENCE" generator="ArticleSeq"/>
                </id>
               ...
            </attributes>
        </entity>
        <entity class="org.mag.pub.Company">
            <table name="COMP"/>
            <attributes>
                <id name="id">
                    <column name="CID"/>
                </id>
                ...
            </attributes>
        </entity>
        <entity class="org.mag.pub.Author">
            <table name="AUTH"/>
            <attributes>
                <id name="id">
                    <column name="AID" column-definition="INTEGER64"/>
                    <generated-value strategy="TABLE" generator="AuthorGen"/>
                    <table-generator name="AuthorGen" table="AUTH_GEN"
                        pk-column-name="PK" value-column-name="AID"/>
                </id>
                ...
            </attributes>
        </entity>
        <entity class="org.mag.subcribe.Contract">
            <table schema="CNTRCT"/>
            <inheritance strategy="JOINED"/>
            <attributes>
                ...
            </attributes>
        </entity>
        <entity class="org.mag.subcribe.Subscription">
            <table name="SUB" schema="CNTRCT"/>
            <inheritance strategy="SINGLE_TABLE"/>
            <attributes>
                <id name="id">
                    <generated-value strategy="IDENTITY"/>
                </id>
                ...
            </attributes>
        </entity>
        <entity class="org.mag.subscribe.Subscription.LineItem">
            <table name="LINE_ITEM" schema="CNTRCT"/>
            <primary-key-join-column name="ID" referenced-column-name="PK"/>
            ...
        </entity>
        <entity class="org.mag.subscribe.LifetimeSubscription" name="Lifetime">
            ...
        </entity>
        <entity class="org.mag.subscribe.TrialSubscription" name="Trial">
            ...
        </entity>
    </entity-mappings>

Prev | Up | Next
5. Generators | Home | 7. Discriminator