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.
<hibernate-mapping> <subclass name="DomesticCat" extends="Cat" discriminator-value="D"> <property name="name" type="string"/> </subclass> </hibernate-mapping>
Suppose we have an interface Payment
, with implementors
CreditCardPayment
, CashPayment
,
ChequePayment
. The table per hierarchy mapping would
look like:
<class name="Payment" table="PAYMENT"> <id name="id" type="long" column="PAYMENT_ID"> <generator class="native"/> </id> <discriminator column="PAYMENT_TYPE" type="string"/> <property name="amount" column="AMOUNT"/> ... <subclass name="CreditCardPayment" discriminator-value="CREDIT"> <property name="creditCardType" column="CCTYPE"/> ... </subclass> <subclass name="CashPayment" discriminator-value="CASH"> ... </subclass> <subclass name="ChequePayment" discriminator-value="CHEQUE"> ... </subclass> </class>
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.
A table per subclass mapping would look like:
<class name="Payment" table="PAYMENT"> <id name="id" type="long" column="PAYMENT_ID"> <generator class="native"/> </id> <property name="amount" column="AMOUNT"/> ... <joined-subclass name="CreditCardPayment" table="CREDIT_PAYMENT"> <key column="PAYMENT_ID"/> <property name="creditCardType" column="CCTYPE"/> ... </joined-subclass> <joined-subclass name="CashPayment" table="CASH_PAYMENT"> <key column="PAYMENT_ID"/> ... </joined-subclass> <joined-subclass name="ChequePayment" table="CHEQUE_PAYMENT"> <key column="PAYMENT_ID"/> ... </joined-subclass> </class>
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).
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:
<class name="Payment" table="PAYMENT"> <id name="id" type="long" column="PAYMENT_ID"> <generator class="native"/> </id> <discriminator column="PAYMENT_TYPE" type="string"/> <property name="amount" column="AMOUNT"/> ... <subclass name="CreditCardPayment" discriminator-value="CREDIT"> <join table="CREDIT_PAYMENT"> <key column="PAYMENT_ID"/> <property name="creditCardType" column="CCTYPE"/> ... </join> </subclass> <subclass name="CashPayment" discriminator-value="CASH"> <join table="CASH_PAYMENT"> <key column="PAYMENT_ID"/> ... </join> </subclass> <subclass name="ChequePayment" discriminator-value="CHEQUE"> <join table="CHEQUE_PAYMENT" fetch="select"> <key column="PAYMENT_ID"/> ... </join> </subclass> </class>
The optional fetch="select"
declaration tells Hibernate
not to fetch the ChequePayment
subclass data using an
outer join when querying the superclass.
You may even mix the table per hierarchy and table per subclass strategies using this approach:
<class name="Payment" table="PAYMENT"> <id name="id" type="long" column="PAYMENT_ID"> <generator class="native"/> </id> <discriminator column="PAYMENT_TYPE" type="string"/> <property name="amount" column="AMOUNT"/> ... <subclass name="CreditCardPayment" discriminator-value="CREDIT"> <join table="CREDIT_PAYMENT"> <property name="creditCardType" column="CCTYPE"/> ... </join> </subclass> <subclass name="CashPayment" discriminator-value="CASH"> ... </subclass> <subclass name="ChequePayment" discriminator-value="CHEQUE"> ... </subclass> </class>
For any of these mapping strategies, a polymorphic association to the root
Payment
class is mapped using
<many-to-one>
.
<many-to-one name="payment" column="PAYMENT_ID" class="Payment"/>
There are two ways we could go about mapping the table per concrete class
strategy. The first is to use <union-subclass>
.
<class name="Payment"> <id name="id" type="long" column="PAYMENT_ID"> <generator class="sequence"/> </id> <property name="amount" column="AMOUNT"/> ... <union-subclass name="CreditCardPayment" table="CREDIT_PAYMENT"> <property name="creditCardType" column="CCTYPE"/> ... </union-subclass> <union-subclass name="CashPayment" table="CASH_PAYMENT"> ... </union-subclass> <union-subclass name="ChequePayment" table="CHEQUE_PAYMENT"> ... </union-subclass> </class>
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.
An alternative approach is to make use of implicit polymorphism:
<class name="CreditCardPayment" table="CREDIT_PAYMENT"> <id name="id" type="long" column="CREDIT_PAYMENT_ID"> <generator class="native"/> </id> <property name="amount" column="CREDIT_AMOUNT"/> ... </class> <class name="CashPayment" table="CASH_PAYMENT"> <id name="id" type="long" column="CASH_PAYMENT_ID"> <generator class="native"/> </id> <property name="amount" column="CASH_AMOUNT"/> ... </class> <class name="ChequePayment" table="CHEQUE_PAYMENT"> <id name="id" type="long" column="CHEQUE_PAYMENT_ID"> <generator class="native"/> </id> <property name="amount" column="CHEQUE_AMOUNT"/> ... </class>
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
UNION
s when performing polymorphic queries.
For this mapping strategy, a polymorphic association to Payment
is usually mapped using <any>
.
<any name="payment" meta-type="string" id-type="long"> <meta-value value="CREDIT" class="CreditCardPayment"/> <meta-value value="CASH" class="CashPayment"/> <meta-value value="CHEQUE" class="ChequePayment"/> <column name="PAYMENT_CLASS"/> <column name="PAYMENT_ID"/> </any>
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.)
<class name="CreditCardPayment" table="CREDIT_PAYMENT"> <id name="id" type="long" column="CREDIT_PAYMENT_ID"> <generator class="native"/> </id> <discriminator column="CREDIT_CARD" type="string"/> <property name="amount" column="CREDIT_AMOUNT"/> ... <subclass name="MasterCardPayment" discriminator-value="MDC"/> <subclass name="VisaPayment" discriminator-value="VISA"/> </class> <class name="NonelectronicTransaction" table="NONELECTRONIC_TXN"> <id name="id" type="long" column="TXN_ID"> <generator class="native"/> </id> ... <joined-subclass name="CashPayment" table="CASH_PAYMENT"> <key column="PAYMENT_ID"/> <property name="amount" column="CASH_AMOUNT"/> ... </joined-subclass> <joined-subclass name="ChequePayment" table="CHEQUE_PAYMENT"> <key column="PAYMENT_ID"/> <property name="amount" column="CHEQUE_AMOUNT"/> ... </joined-subclass> </class>
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
.