492 lines
19 KiB
XML
492 lines
19 KiB
XML
<chapter id="inheritance">
|
|
<title>Inheritance Mapping</title>
|
|
|
|
<sect1 id="inheritance-strategies" revision="3">
|
|
<title>The Three Strategies</title>
|
|
|
|
<para>
|
|
Hibernate supports the three basic inheritance mapping strategies:
|
|
</para>
|
|
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para>
|
|
table per class hierarchy
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
table per subclass
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
table per concrete class
|
|
</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
|
|
<para>
|
|
In addition, Hibernate supports a fourth, slightly different kind of
|
|
polymorphism:
|
|
</para>
|
|
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para>
|
|
implicit polymorphism
|
|
</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
|
|
<para>
|
|
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 <literal><subclass></literal>,
|
|
and <literal><joined-subclass></literal> and
|
|
<literal><union-subclass></literal> mappings under the same root
|
|
<literal><class></literal> element. It is possible to mix together
|
|
the table per hierarchy and table per subclass strategies, under the
|
|
the same <literal><class></literal> element, by combining the
|
|
<literal><subclass></literal> and <literal><join></literal>
|
|
elements (see below).
|
|
</para>
|
|
|
|
<para>
|
|
It is possible to define <literal>subclass</literal>, <literal>union-subclass</literal>,
|
|
and <literal>joined-subclass</literal> mappings in separate mapping documents, directly beneath
|
|
<literal>hibernate-mapping</literal>. This allows you to extend a class hierachy just by adding
|
|
a new mapping file. You must specify an <literal>extends</literal> 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.
|
|
</para>
|
|
|
|
<programlisting><![CDATA[
|
|
<hibernate-mapping>
|
|
<subclass name="DomesticCat" extends="Cat" discriminator-value="D">
|
|
<property name="name" type="string"/>
|
|
</subclass>
|
|
</hibernate-mapping>]]></programlisting>
|
|
|
|
|
|
<sect2 id="inheritance-tableperclass" >
|
|
<title>Table per class hierarchy</title>
|
|
|
|
<para>
|
|
Suppose we have an interface <literal>Payment</literal>, with implementors
|
|
<literal>CreditCardPayment</literal>, <literal>CashPayment</literal>,
|
|
<literal>ChequePayment</literal>. The table per hierarchy mapping would
|
|
look like:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[<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>]]></programlisting>
|
|
|
|
<para>
|
|
Exactly one table is required. There is one big limitation of this mapping
|
|
strategy: columns declared by the subclasses, such as <literal>CCTYPE</literal>,
|
|
may not have <literal>NOT NULL</literal> constraints.
|
|
</para>
|
|
|
|
</sect2>
|
|
|
|
<sect2 id="inheritance-tablepersubclass">
|
|
<title>Table per subclass</title>
|
|
|
|
<para>
|
|
A table per subclass mapping would look like:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[<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>]]></programlisting>
|
|
|
|
<para>
|
|
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).
|
|
</para>
|
|
|
|
</sect2>
|
|
|
|
<sect2 id="inheritance-tablepersubclass-discriminator" revision="2">
|
|
<title>Table per subclass, using a discriminator</title>
|
|
|
|
<para>
|
|
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 <literal><subclass></literal> and
|
|
<literal><join></literal>, as follow:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[<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>]]></programlisting>
|
|
|
|
<para>
|
|
The optional <literal>fetch="select"</literal> declaration tells Hibernate
|
|
not to fetch the <literal>ChequePayment</literal> subclass data using an
|
|
outer join when querying the superclass.
|
|
</para>
|
|
|
|
</sect2>
|
|
|
|
<sect2 id="inheritance-mixing-tableperclass-tablepersubclass">
|
|
<title>Mixing table per class hierarchy with table per subclass</title>
|
|
|
|
<para>
|
|
You may even mix the table per hierarchy and table per subclass strategies
|
|
using this approach:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[<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>]]></programlisting>
|
|
|
|
<para>
|
|
For any of these mapping strategies, a polymorphic association to the root
|
|
<literal>Payment</literal> class is mapped using
|
|
<literal><many-to-one></literal>.
|
|
</para>
|
|
|
|
<programlisting><![CDATA[<many-to-one name="payment" column="PAYMENT_ID" class="Payment"/>]]></programlisting>
|
|
|
|
</sect2>
|
|
|
|
<sect2 id="inheritance-tableperconcrete" revision="2">
|
|
<title>Table per concrete class</title>
|
|
|
|
<para>
|
|
There are two ways we could go about mapping the table per concrete class
|
|
strategy. The first is to use <literal><union-subclass></literal>.
|
|
</para>
|
|
|
|
<programlisting><![CDATA[<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>]]></programlisting>
|
|
|
|
<para>
|
|
Three tables are involved for the subclasses. Each table defines columns for
|
|
all properties of the class, including inherited properties.
|
|
</para>
|
|
|
|
<para>
|
|
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.
|
|
</para>
|
|
|
|
<para>
|
|
If your superclass is abstract, map it with <literal>abstract="true"</literal>.
|
|
Of course, if it is not abstract, an additional table (defaults to
|
|
<literal>PAYMENT</literal> in the example above) is needed to hold instances
|
|
of the superclass.
|
|
</para>
|
|
|
|
</sect2>
|
|
|
|
<sect2 id="inheritance-tableperconcreate-polymorphism">
|
|
<title>Table per concrete class, using implicit polymorphism</title>
|
|
|
|
<para>
|
|
An alternative approach is to make use of implicit polymorphism:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[<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>]]></programlisting>
|
|
|
|
<para>
|
|
Notice that nowhere do we mention the <literal>Payment</literal> interface
|
|
explicitly. Also notice that properties of <literal>Payment</literal> are
|
|
mapped in each of the subclasses. If you want to avoid duplication, consider
|
|
using XML entities
|
|
(e.g. <literal>[ <!ENTITY allproperties SYSTEM "allproperties.xml"> ]</literal>
|
|
in the <literal>DOCTYPE</literal> declartion and
|
|
<literal>&allproperties;</literal> in the mapping).
|
|
</para>
|
|
|
|
<para>
|
|
The disadvantage of this approach is that Hibernate does not generate SQL
|
|
<literal>UNION</literal>s when performing polymorphic queries.
|
|
</para>
|
|
|
|
<para>
|
|
For this mapping strategy, a polymorphic association to <literal>Payment</literal>
|
|
is usually mapped using <literal><any></literal>.
|
|
</para>
|
|
|
|
<programlisting><![CDATA[<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>]]></programlisting>
|
|
|
|
</sect2>
|
|
|
|
<sect2 id="inheritace-mixingpolymorphism">
|
|
<title>Mixing implicit polymorphism with other inheritance mappings</title>
|
|
|
|
<para>
|
|
There is one further thing to notice about this mapping. Since the subclasses
|
|
are each mapped in their own <literal><class></literal> element (and since
|
|
<literal>Payment</literal> 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 <literal>Payment</literal> interface.)
|
|
</para>
|
|
|
|
<programlisting><![CDATA[<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>]]></programlisting>
|
|
|
|
<para>
|
|
Once again, we don't mention <literal>Payment</literal> explicitly. If we
|
|
execute a query against the <literal>Payment</literal> interface - for
|
|
example, <literal>from Payment</literal> - Hibernate
|
|
automatically returns instances of <literal>CreditCardPayment</literal>
|
|
(and its subclasses, since they also implement <literal>Payment</literal>),
|
|
<literal>CashPayment</literal> and <literal>ChequePayment</literal> but
|
|
not instances of <literal>NonelectronicTransaction</literal>.
|
|
</para>
|
|
|
|
</sect2>
|
|
|
|
</sect1>
|
|
|
|
<sect1 id="inheritance-limitations">
|
|
<title>Limitations</title>
|
|
|
|
<para>
|
|
There are certain limitations to the "implicit polymorphism" approach to
|
|
the table per concrete-class mapping strategy. There are somewhat less
|
|
restrictive limitations to <literal><union-subclass></literal>
|
|
mappings.
|
|
</para>
|
|
|
|
<para>
|
|
The following table shows the limitations of table per concrete-class
|
|
mappings, and of implicit polymorphism, in Hibernate.
|
|
</para>
|
|
|
|
<table frame="topbot">
|
|
<title>Features of inheritance mappings</title>
|
|
<tgroup cols='8' align='left' colsep='1' rowsep='1'>
|
|
<colspec colname='c1' colwidth="1*"/>
|
|
<colspec colname='c2' colwidth="1*"/>
|
|
<colspec colname='c3' colwidth="1*"/>
|
|
<colspec colname='c4' colwidth="1*"/>
|
|
<colspec colname='c5' colwidth="1*"/>
|
|
<colspec colname='c6' colwidth="1*"/>
|
|
<colspec colname='c7' colwidth="1*"/>
|
|
<colspec colname='c8' colwidth="1*"/>
|
|
<thead>
|
|
<row>
|
|
<entry>Inheritance strategy</entry>
|
|
<entry>Polymorphic many-to-one</entry>
|
|
<entry>Polymorphic one-to-one</entry>
|
|
<entry>Polymorphic one-to-many</entry>
|
|
<entry>Polymorphic many-to-many</entry>
|
|
<entry>Polymorphic <literal>load()/get()</literal></entry>
|
|
<entry>Polymorphic queries</entry>
|
|
<entry>Polymorphic joins</entry>
|
|
<entry>Outer join fetching</entry>
|
|
</row>
|
|
</thead>
|
|
<tbody>
|
|
<row>
|
|
<entry>table per class-hierarchy</entry>
|
|
<entry><literal><many-to-one></literal></entry>
|
|
<entry><literal><one-to-one></literal></entry>
|
|
<entry><literal><one-to-many></literal></entry>
|
|
<entry><literal><many-to-many></literal></entry>
|
|
<entry><literal>s.get(Payment.class, id)</literal></entry>
|
|
<entry><literal>from Payment p</literal></entry>
|
|
<entry><literal>from Order o join o.payment p</literal></entry>
|
|
<entry><emphasis>supported</emphasis></entry>
|
|
</row>
|
|
<row>
|
|
<entry>table per subclass</entry>
|
|
<entry><literal><many-to-one></literal></entry>
|
|
<entry><literal><one-to-one></literal></entry>
|
|
<entry><literal><one-to-many></literal></entry>
|
|
<entry><literal><many-to-many></literal></entry>
|
|
<entry><literal>s.get(Payment.class, id)</literal></entry>
|
|
<entry><literal>from Payment p</literal></entry>
|
|
<entry><literal>from Order o join o.payment p</literal></entry>
|
|
<entry><emphasis>supported</emphasis></entry>
|
|
</row>
|
|
<row>
|
|
<entry>table per concrete-class (union-subclass)</entry>
|
|
<entry><literal><many-to-one></literal></entry>
|
|
<entry><literal><one-to-one></literal></entry>
|
|
<entry><literal><one-to-many></literal> (for <literal>inverse="true"</literal> only)</entry>
|
|
<entry><literal><many-to-many></literal></entry>
|
|
<entry><literal>s.get(Payment.class, id)</literal></entry>
|
|
<entry><literal>from Payment p</literal></entry>
|
|
<entry><literal>from Order o join o.payment p</literal></entry>
|
|
<entry><emphasis>supported</emphasis></entry>
|
|
</row>
|
|
<row>
|
|
<entry>table per concrete class (implicit polymorphism)</entry>
|
|
<entry><literal><any></literal></entry>
|
|
<entry><emphasis>not supported</emphasis></entry>
|
|
<entry><emphasis>not supported</emphasis></entry>
|
|
<entry><literal><many-to-any></literal></entry>
|
|
<entry><literal>s.createCriteria(Payment.class).add( Restrictions.idEq(id) ).uniqueResult()</literal></entry>
|
|
<entry><literal>from Payment p</literal></entry>
|
|
<entry><emphasis>not supported</emphasis></entry>
|
|
<entry><emphasis>not supported</emphasis></entry>
|
|
</row>
|
|
</tbody>
|
|
</tgroup>
|
|
</table>
|
|
|
|
</sect1>
|
|
|
|
</chapter>
|