Inheritance Mapping
The Three Strategies
Hibernate supports the three basic inheritance mapping strategies:
table per class hierarchy
table per subclass
table per concrete class
In addition, Hibernate supports a fourth, slightly different kind of
polymorphism:
implicit polymorphism
It is possible to use different mapping strategies for different
branches of the same inheritance hierarchy, and then make use of implicit
polymorphism to achieve polymorphism across the whole hierarchy. However,
Hibernate does not support mixing <subclass>,
and <joined-subclass> and
<union-subclass> mappings under the same root
<class> element. It is possible to mix together
the table per hierarchy and table per subclass strategies, under the
the same <class> element, by combining the
<subclass> and <join>
elements (see below).
It is possible to define subclass, union-subclass,
and joined-subclass mappings in separate mapping documents, directly beneath
hibernate-mapping. This allows you to extend a class hierachy just by adding
a new mapping file. You must specify an extends attribute in the subclass mapping,
naming a previously mapped superclass. Note: Previously this feature made the ordering of the mapping
documents important. Since Hibernate3, the ordering of mapping files does not matter when using the
extends keyword. The ordering inside a single mapping file still needs to be defined as superclasses
before subclasses.
]]>
Table per class hierarchy
Suppose we have an interface Payment, with implementors
CreditCardPayment, CashPayment,
ChequePayment. The table per hierarchy mapping would
look like:
...
...
...
...
]]>
Exactly one table is required. There is one big limitation of this mapping
strategy: columns declared by the subclasses, such as CCTYPE,
may not have NOT NULL constraints.
Table per subclass
A table per subclass mapping would look like:
...
...
...
...
]]>
Four tables are required. The three subclass tables have primary
key associations to the superclass table (so the relational model
is actually a one-to-one association).
Table per subclass, using a discriminator
Note that Hibernate's implementation of table per subclass requires
no discriminator column. Other object/relational mappers use a
different implementation of table per subclass which requires a type
discriminator column in the superclass table. The approach taken by
Hibernate is much more difficult to implement but arguably more
correct from a relational point of view. If you would like to use
a discriminator column with the table per subclass strategy, you
may combine the use of <subclass> and
<join>, as follow:
...
...
...
...
]]>
The optional fetch="select" declaration tells Hibernate
not to fetch the ChequePayment subclass data using an
outer join when querying the superclass.
Mixing table per class hierarchy with table per subclass
You may even mix the table per hierarchy and table per subclass strategies
using this approach:
...
...
...
...
]]>
For any of these mapping strategies, a polymorphic association to the root
Payment class is mapped using
<many-to-one>.
]]>
Table per concrete class
There are two ways we could go about mapping the table per concrete class
strategy. The first is to use <union-subclass>.
...
...
...
...
]]>
Three tables are involved for the subclasses. Each table defines columns for
all properties of the class, including inherited properties.
The limitation of this approach is that if a property is mapped on the
superclass, the column name must be the same on all subclass tables.
(We might relax this in a future release of Hibernate.) The identity
generator strategy is not allowed in union subclass inheritance, indeed
the primary key seed has to be shared accross all unioned subclasses
of a hierarchy.
If your superclass is abstract, map it with abstract="true".
Of course, if it is not abstract, an additional table (defaults to
PAYMENT in the example above) is needed to hold instances
of the superclass.
Table per concrete class, using implicit polymorphism
An alternative approach is to make use of implicit polymorphism:
...
...
...
]]>
Notice that nowhere do we mention the Payment interface
explicitly. Also notice that properties of Payment are
mapped in each of the subclasses. If you want to avoid duplication, consider
using XML entities
(e.g. [ <!ENTITY allproperties SYSTEM "allproperties.xml"> ]
in the DOCTYPE declartion and
&allproperties; in the mapping).
The disadvantage of this approach is that Hibernate does not generate SQL
UNIONs when performing polymorphic queries.
For this mapping strategy, a polymorphic association to Payment
is usually mapped using <any>.
]]>
Mixing implicit polymorphism with other inheritance mappings
There is one further thing to notice about this mapping. Since the subclasses
are each mapped in their own <class> element (and since
Payment is just an interface), each of the subclasses could
easily be part of another inheritance hierarchy! (And you can still use polymorphic
queries against the Payment interface.)
...
...
...
...
]]>
Once again, we don't mention Payment explicitly. If we
execute a query against the Payment interface - for
example, from Payment - Hibernate
automatically returns instances of CreditCardPayment
(and its subclasses, since they also implement Payment),
CashPayment and ChequePayment but
not instances of NonelectronicTransaction.
Limitations
There are certain limitations to the "implicit polymorphism" approach to
the table per concrete-class mapping strategy. There are somewhat less
restrictive limitations to <union-subclass>
mappings.
The following table shows the limitations of table per concrete-class
mappings, and of implicit polymorphism, in Hibernate.
Features of inheritance mappings
Inheritance strategy
Polymorphic many-to-one
Polymorphic one-to-one
Polymorphic one-to-many
Polymorphic many-to-many
Polymorphic load()/get()
Polymorphic queries
Polymorphic joins
Outer join fetching
table per class-hierarchy
<many-to-one>
<one-to-one>
<one-to-many>
<many-to-many>
s.get(Payment.class, id)
from Payment p
from Order o join o.payment p
supported
table per subclass
<many-to-one>
<one-to-one>
<one-to-many>
<many-to-many>
s.get(Payment.class, id)
from Payment p
from Order o join o.payment p
supported
table per concrete-class (union-subclass)
<many-to-one>
<one-to-one>
<one-to-many> (for inverse="true" only)
<many-to-many>
s.get(Payment.class, id)
from Payment p
from Order o join o.payment p
supported
table per concrete class (implicit polymorphism)
<any>
not supported
not supported
<many-to-any>
s.createCriteria(Payment.class).add( Restrictions.idEq(id) ).uniqueResult()
from Payment p
not supported
not supported