15.8. Inheritance

15.8.1. subclass-table
15.8.1.1. Advantages
15.8.1.2. Disadvantages
15.8.1.3. Additional Considerations
15.8.2. new-table
15.8.2.1. Joined
15.8.2.1.1. Advantages
15.8.2.1.2. Disadvantages
15.8.2.2. Table Per Class
15.8.2.2.1. Advantages
15.8.2.2.2. Disadvantages
15.8.3. superclass-table
15.8.3.1. Advantages
15.8.3.2. Disadvantages
15.8.4. Putting it All Together

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, JDOR provides very flexible object-relational mapping of inheritance trees, making the best of a bad situation. Each class in an inheritance hierarchy can use the inheritance element to describe its inheritance mapping. This element has a single attribute: strategy. In the list that follows, we examine JDOR's standard inheritance strategies. Some vendors may define additional proprietary strategies.

[Note]Note

Kodo does not define any non-standard inheritance models, but does allow you to create your own custom mappings. See the Reference Guide's Section 7.10, “Custom Mappings” for details on writing custom inheritance strategies and pluggin them in to Kodo.

15.8.1. subclass-table

The subclass-table inheritance strategy indicates that the current class is not mapped to any table at all, and that all of its fields must be mapped by subclasses into their own tables. This strategy is typically used with abstract base classes that are not represented in the relational schema. Classes that declare an inheritance strategy of subclass-table should not define the class element's table attribute, nor should they attempt to define mappings for any fields.

In our model, the abstract Contract class uses the subclass-table inheritance strategy. Contract's two direct subclasses, Subscription and LineItem, each map all of Contract's fields into their own tables.

Example 15.4. subclass-table Mapping

<class name="Contract">
    <inheritance strategy="subclass-table"/>
    ...
</class>
[Note]Note

The pattern of mapping the fields of an abstract, unmapped base class into the tables of each subclass is often referred to as a horizontal or distributed inheritance mapping.

15.8.1.1. Advantages

An advantage of using the subclass-table strategy for abstract base classes is that properties common to multiple persistent subclasses can be defined in the superclass without having to suffer the performance consequences and relational design restrictions inherent in other strategies (which we will examine shortly). Persisting and modifying instances of subclasses is efficient, typically only requiring a single INSERT or UPDATE statement. Loading relations to these subclasses is also efficient.

15.8.1.2. Disadvantages

Though relations to mapped subclasses of a subclass-table class are very efficient, relations to the unmapped base class itself are equally inefficient. When the concrete subclass is not known, the related object could be in any of the subclass tables, making joins through the relation impossible. Subclasses can even use repeated primary key values, forcing the JDOR implementation to record more than just the related primary key values in the database.

This ambiguity also affects queries and identity lookups: queries against a subclass-table base class require either multiple SELECT statements (one for each mapped subclass), or a complex SQL UNION.

[Note]Note

Kodo provides metadata extensions you can use to indicate that a field declared as a relation to a base class is actually a relation to a specific subclass. These extensions often alleviate the need for mapping a relation to a subclass-table type. See Section 6.4, “Metadata Extensions”.

When a relation to a subclass-table class cannot be avoided, Kodo stores either the related object's primary key values or its stringified object id. If the type uses datastore identity or has a concrete application identity class, Kodo stores the primary key values. If the type uses application identity and has an abstract identity class, Kodo must resort to storing the stringified identity object.

15.8.1.3. Additional Considerations

Here are some additional caveats to consider when using the subclass-table inheritance mapping:

  • Declaring classes abstract. You are not required to make all subclass-table classes abstract. However, we recommend that you do so, as any attempt to persist a subclass-table base class instance will result in an exception on flush.

  • Application identity. When a subclass-table superclass uses application identity, you must observe one of the following two restrictions:

    1. The primary key values in each of the tables that extend the subclass-table superclass must be unique. In our model, that means that there can be no row in CNTRCT.SUB with the same primary key value as a row in CNTRCT.LINE_ITEM. This is because a call to getObjectById with an application identity instance cannot identify which subclass the ID is associated with.

    2. Rather than having a single application identity class associated with the entire class hierarchy, each subclass declares its own application identity class. The inheritance hierarchy of the application identity classes must exactly match the inheritance hierarchy of the persistent classes they represent, as discussed in Section 4.5.2.1, “Application Identity Hierarchies”.

15.8.2. new-table

The new-table inheritance strategy employs a new table to hold the fields of the class. You must specify the table name in the class element's table attribute.

new-table is the default strategy for base persistent classes and for subclasses of subclass-table classes. Classes that extend non-subclass-table base classes can also use this strategy to map their fields to a new table, rather than to the superclass table. The subclass table might contain only subclass state, or might re-map all superclass state as well.

15.8.2.1. Joined

When the subclass table only contains subclass state, the JDOR implementation must be able to link corresponding records in the superclass and subclass tables together to retrieve all of the persistent state for a subclass instance. You tell the JDOR implementation how to do this by nesting a join element in your inheritance element. Thus, this is often referred to as a joined inheritance strategy.

As its name implies, the join element maps a logical foreign key from the subclass table to the superclass table. It has a column attribute and nested column elements for representing the subclass table's foreign key columns. Section 15.7, “Joins” demonstrated how to use columns to map joins. Typically, the subclass table's foreign key columns are also that table's primary key columns.

[Note]Note

Using joined subclass tables is also called vertical inheritance mapping.

All of the classes in our model except Contract and LifetimeSubscription (not pictured) use the new-table strategy. Most of these classes are either base classes or direct subclasses of Contract, a subclass-table class. In these cases, the new-table strategy is the default. TrialSubscription, however, extends Subscription, which is itself mapped to a table. Therefore, TrialSubscription could have mapped its fields to Subscription's table.

Instead, TrialSubscription maps its declared fields to the CNTRCT.TRIAL_SUB table, and joins to the CNTRCT.SUB table for base class state. The join is made by linking the CNTRCT.TRIAL_SUB.ID foreign key column to the CNTRCT.SUB.ID primary key column. The example below shows how to represent this in mapping metadata.

Example 15.5. Joined Subclass Tables

<class name="TrialSubscription" table="CNTRCT.TRIAL_SUB">
    <inheritance strategy="new-table">
       <join>
           <column name="ID" target="CNTRCT.SUB.ID"/> 
       </join>
    </inheritance>
    ...
</class>

That is the long version, however. JDOR is smart enough to default the target table to the superclass table, and we can apply join shortcuts to yield a much more concise representation:

<class name="TrialSubscription" table="CNTRCT.TRIAL_SUB">
    <inheritance strategy="new-table">
       <join column="ID"/>
    </inheritance>
    ...
</class>
15.8.2.1.1. Advantages

If you want to map a class to a table and none of the superclasses of that class are themselves mapped to a table (or the class has no persistent superclasses), then the new-table strategy is your only choice. It is only meaningful to discuss the advantages and disadvantages of the new-table strategy for classes that have a superclass mapped to a different table. For example, our model's TrialSubscription class extends Subscription, yet maps its declared fields to a different table. We could have mapped TrialSubscription's fields to Subscription's table; what made us choose to to use a separate joined table instead?

  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 the new-table strategy can be loaded through standard joins and can use standard foreign keys, as opposed to the machinations required to load relations to subclass-table base types.

15.8.2.1.2. Disadvantages

Using multiple joined tables slows things down. 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]Note

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

15.8.2.2. Table Per Class

Like the joined new-table strategy, the table-per-class new-table strategy maps a subclass to its own table. Unlike the joined strategy, however, the subclass table includes all state for an instance of the corresponding class. Thus to load a subclass instance, the JDOR 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.

Example 15.6. Table Per Class Mapping

<class name="Tabloid" table="TABLOID">
    <inheritance strategy="new-table"/>
    <field name="Magazine.isbn" column="ISBN"/>
    <field name="Magazine.title" column="TITLE"/>
    ...
    <field name="data" column="TAB_DATA"/>
</class>

Notice that only the lack of a join within the inheritance element differentiates a table-per-class strategy from a joined strategy. Also, notice that a table-per-class subclass explicitly re-maps all of its inherited fields into its own table.

15.8.2.2.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.

15.8.2.2.2. Disadvantages

Polymorphic relations to a non-leaf classes in a table-per-class hierarchy have many limitations. In some ways, they are similar to relations to a subclass-table base class. 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.

[Note]Note

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

15.8.3. superclass-table

superclass-table is the default strategy for subclasses of new-table and other superclass-table classes. In this strategy, the subclass' fields are mapped to superclass' table. Classes that use the superclass-table inheritance strategy should not specify the class element's table attribute.

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 15.7. superclass-table Mapping

<class name="LifetimeSubscription">
    <inheritance strategy="superclass-table"/>
    ...
</class>
[Note]Note

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

15.8.3.1. Advantages

superclass-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 superclass-table inheritance hierarchy are just as efficient as relations to a base class.

15.8.3.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.

15.8.4. Putting it All Together

Now that we have covered JDOR'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 15.8. Inheritance Mapping

<?xml version="1.0"?>
<orm>
    <package name="org.mag">
        <sequence name="ArticleSeq" datastore-sequence="ART_SEQ"/> 
        <class name="Magazine" table="MAG">
            <!-- not strictly necessary, since this is the default -->
            <inheritance strategy="new-table"/>
            <field name="isbn">
                <column name="ISBN" jdbc-type="char" length="15"/>
            </field>
            <field name="title" column="TITLE"/>
            ...
        </class>
        <class name="Article" table="ART">
            <!-- not strictly necessary, since this is the default -->
            <inheritance strategy="new-table"/>
            <field name="id" column="ID"/>
            ...
        </class>    
    </package>
    <package name="org.mag.pub">
        <sequence name="AuthorSeq" factory-class="Author$SequenceFactory"/>
        <class name="Company" table="COMP">
            <datastore-identity column="CID" strategy="autoassign"/>
            <!-- not strictly necessary, since this is the default -->
            <inheritance strategy="new-table"/>
            ...
        </class>    
        <class name="Author" table="AUTH">
            <datastore-identity sequence="AuthorSeq">
                <column name="AID" sql-type="INTEGER64"/>
            </datastore-identity>
            <!-- not strictly necessary, since this is the default -->
            <inheritance strategy="new-table"/>
            ...
        </class>
    </package>
    <package name="org.mag.subscribe">
        <sequence name="ContractSeq" strategy="transactional"/> 
        <class name="Contract">
            <inheritance strategy="subclass-table"/>
            ...
        </class>
        <class name="Subscription" table="CNTRCT.SUB">
            <!-- not strictly necessary, since this is the default -->
            <inheritance strategy="new-table"/>
            <field name="Contract.id" column="ID"/>
            ...
        </class>
        <class name="LifetimeSubscription">
            <!-- not strictly necessary, since this is the default -->
            <inheritance strategy="superclass-table"/>
            ...
        </class>
        <class name="TrialSubscription" table="CNTRCT.TRIAL_SUB">
            <inheritance strategy="new-table">
                <join column="ID"/>
            </inheritance>
            ...
        </class>
        <class name="Subscription$LineItem" table="CNTRCT.LINE_ITEM">
            <!-- not strictly necessary, since this is the default -->
            <inheritance strategy="new-table"/>
            <field name="Contract.id" column="ID"/>
            ...
        </class>
    </package>
</orm>

 

Skip navigation bar   Back to Top