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
element to describe its inheritance mapping. This element has a
strategy. In the list that
follows, we examine JDOR's standard inheritance strategies. Some
vendors may define additional proprietary strategies.
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.
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
attribute, nor should they attempt to define mappings for any
In our model, the abstract
subclass-table inheritance strategy.
Contract's two direct subclasses,
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>
An advantage of using the
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
UPDATE statement. Loading
relations to these subclasses is also efficient.
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
(one for each mapped subclass), or a complex SQL
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
When a relation to a
Here are some additional caveats to consider when using
subclass-table inheritance mapping:
Declaring classes abstract.
You are not required to make all
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.
The primary key values in each of the tables
that extend the
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
This is because a call to
getObjectById with an
application identity instance cannot identify
which subclass the ID is associated with.
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 184.108.40.206, “Application Identity Hierarchies”.
new-table inheritance strategy employs a
new table to hold the fields of the class. You must specify the
table name in the
new-table is the default strategy for base
persistent classes and for subclasses of
classes. Classes that extend non-
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.
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
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
columns to map joins. Typically, the
subclass table's foreign key columns are also that table's
primary key columns.
All of the classes in our model except
(not pictured) use the
Most of these classes are either base classes or direct
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,
could have mapped its fields to
TrialSubscription maps its
declared fields to the
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>
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
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?
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.
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
Using multiple joined tables slows things down.
Retrieving any subclass requires one or more database joins,
and storing subclasses requires multiple
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
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.
Like the joined
new-table strategy, the
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
Suppose that our sample model's
class has a subclass
classes are mapped using the table-per-class strategy, as in
the diagram above. In a table-per-class mapping,
all state declared in the base
Tabloid maps to a separate table,
TABLOID. This table contains not only the
state declared in the
but all the base class state from
as well. Thus the
table would contain columns for
title, and other
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
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.
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.
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
(one for each possible subclass), or a complex
Section 7.8.1, “Table Per Class” in the Reference Guide describes the limitations Kodo places on table-per-class mapping.
superclass-table is the default strategy for
new-table and other
superclass-table classes. In this strategy,
the subclass' fields are mapped to superclass' table. Classes
that use the
strategy should not specify the
In our model,
Subscription is mapped to the
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>
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
UPDATE statement. Finally, relations to
any class within a
inheritance hierarchy are just as efficient as relations to
a base class.
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.
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>