revised inheritance and components, minor fixes to example

git-svn-id: https://svn.jboss.org/repos/hibernate/trunk/Hibernate3/doc@5573 1b8cb986-b30d-0410-93ca-fae66ebed9b2
This commit is contained in:
Gavin King 2005-02-06 00:27:53 +00:00
parent 0b8ee71d7f
commit 35cf78f98a
3 changed files with 278 additions and 146 deletions

View File

@ -236,7 +236,7 @@
<title>Components as Map indices</title>
<para>
The <literal>&lt;composite-index&gt;</literal> element lets you map a
The <literal>&lt;composite-map-key&gt;</literal> element lets you map a
component class as the key of a <literal>Map</literal>. Make sure you override
<literal>hashCode()</literal> and <literal>equals()</literal> correctly on
the component class.
@ -266,92 +266,110 @@
</listitem>
</itemizedlist>
<para>
<emphasis>Note: in Hibernate3, the second requirement is not an absolutely hard
requirement of Hibernate. But do it anyway.</emphasis>
</para>
<para>
You can't use an <literal>IdentifierGenerator</literal> to generate composite keys.
Instead the application must assign its own identifiers.
</para>
<para>
Since a composite identifier must be assigned to the object before saving it,
we can't use <literal>unsaved-value</literal> of the identifier to distinguish
between newly instantiated transient instances and detached instances from a
previous session.
</para>
<para>
TODO: document new auto-detect features for assigned IDs in H3
</para>
<para>
So, if you wish to use transitive reattachment (you don't have to), you must
either implement <literal>Interceptor.isUnsaved()</literal> or define the
<literal>unsaved-value</literal> of a <literal>&lt;version&gt;</literal> or
<literal>&lt;timestamp&gt;</literal> element.
</para>
<para>
Use the <literal>&lt;composite-id&gt;</literal> tag (with nested
<literal>&lt;key-property&gt;</literal> elements) in place of the
usual <literal>&lt;id&gt;</literal> declaration:
<literal>&lt;key-property&gt;</literal> elements) in place of the usual
<literal>&lt;id&gt;</literal> declaration. For example, the
<literal>OrderLine</literal> class has a primary key that depends upon
the (composite) primary key of <literal>Order</literal>.
</para>
<programlisting><![CDATA[<class name="eg.Foo" table"FOOS">
<composite-id name="compId" class="eg.FooCompositeID">
<key-property name="string"/>
<key-property name="short"/>
<key-property name="date" column="date_" type="date"/>
<programlisting><![CDATA[<class name="OrderLine">
<composite-id name="id" class="OrderLineId">
<key-property name="lineId"/>
<key-property name="orderId"/>
<key-property name="customerId"/>
</composite-id>
<property name="name"/>
<many-to-one name="order" class="Order"
insert="false" update="false">
<column name="orderId"/>
<column name="customerId"/>
</many-to-one>
....
</class>]]></programlisting>
<para>
Now, any foreign keys into the table <literal>FOOS</literal> are also composite.
You must declare this in your mappings for other classes. An association to
<literal>Foo</literal> would be declared like this:
Now, any foreign keys referencing the <literal>OrderLine</literal> table are also
composite. You must declare this in your mappings for other classes. An association
to <literal>OrderLine</literal> would be mapped like this:
</para>
<programlisting><![CDATA[<many-to-one name="foo" class="eg.Foo">
<programlisting><![CDATA[<many-to-one name="orderLine" class="OrderLine">
<!-- the "class" attribute is optional, as usual -->
<column name="foo_string"/>
<column name="foo_short"/>
<column name="foo_date"/>
<column name="lineId"/>
<column name="orderId"/>
<column name="customerId"/>
</many-to-one>]]></programlisting>
<para>
This new <literal>&lt;column&gt;</literal> tag is also used by multi-column custom types.
Actually it is an alternative to the <literal>column</literal> attribute everywhere. A
collection with elements of type <literal>Foo</literal> would use:
(Note that the <literal>&lt;column&gt;</literal> tag is an alternative to the
<literal>column</literal> attribute everywhere.)
</para>
<programlisting><![CDATA[<set name="foos">
<key column="owner_id"/>
<many-to-many class="eg.Foo">
<column name="foo_string"/>
<column name="foo_short"/>
<column name="foo_date"/>
<para>
A <literal>many-to-many</literal> association to <literal>OrderLine</literal> also
uses the composite foreign key:
</para>
<programlisting><![CDATA[<set name="undeliveredOrderLines">
<key column name="warehouseId"/>
<many-to-many class="OrderLine">
<column name="lineId"/>
<column name="orderId"/>
<column name="customerId"/>
</many-to-many>
</set>]]></programlisting>
<para>
On the other hand, <literal>&lt;one-to-many&gt;</literal>, as usual, declares no columns.
The collection of <literal>OrderLine</literal>s in <literal>Order</literal> would
use:
</para>
<programlisting><![CDATA[<set name="orderLines" inverse="true">
<key>
<column name="orderId"/>
<column name="customerId"/>
</key>
<one-to-many class="OrderLine"/>
</set>]]></programlisting>
<para>
(The <literal>&lt;one-to-many&gt;</literal> element, as usual, declares no columns.)
</para>
<para>
If <literal>Foo</literal> itself contains collections, they will also need a
composite foreign key.
If <literal>OrderLine</literal> itself owns a collection, it also has a composite
foreign key.
</para>
<programlisting><![CDATA[<class name="eg.Foo">
<programlisting><![CDATA[<class name="OrderLine">
....
....
<set name="dates" lazy="true">
<list name="deliveryAttempts">
<key> <!-- a collection inherits the composite key type -->
<column name="foo_string"/>
<column name="foo_short"/>
<column name="foo_date"/>
<column name="lineId"/>
<column name="orderId"/>
<column name="customerId"/>
</key>
<element column="foo_date" type="date"/>
<list-index column="attemptId" base="1"/>
<composite-element class="DeliveryAttempt">
...
</composite-element>
</set>
</class>]]></programlisting>
@ -367,7 +385,7 @@
<programlisting><![CDATA[<dynamic-component name="userAttributes">
<property name="foo" column="FOO"/>
<property name="bar" column="BAR"/>
<many-to-one name="baz" class="eg.Baz" column="BAZ"/>
<many-to-one name="baz" class="Baz" column="BAZ_ID"/>
</dynamic-component>]]></programlisting>
<para>

View File

@ -154,12 +154,8 @@ create sequence employer_id_seq]]></programlisting>
<property name="title"/>
<set name="authors" table="author_work">
<key>
<column name="work_id" not-null="true"/>
</key>
<many-to-many class="Author">
<column name="author_id" not-null="true"/>
</many-to-many>
<key column name="work_id"/>
<many-to-many class="Author" column name="author_id"/>
</set>
<subclass name="Book" discriminator-value="B">
@ -292,7 +288,7 @@ alter table author_work
<many-to-one name="customer" column="customer_id"/>
<list name="lineItems" table="line_items">
<key column="order_id"/>
<index column="line_number"/>
<list-index column="line_number"/>
<composite-element class="LineItem">
<property name="quantity"/>
<many-to-one name="product" column="product_id"/>

View File

@ -5,12 +5,7 @@
<title>The Three Strategies</title>
<para>
TODO: While this is all still supported, many new features would require
a rewrite of this whole chapter
</para>
<para>
Hibernate supports the three basic inheritance mapping strategies.
Hibernate supports the three basic inheritance mapping strategies:
</para>
<itemizedlist>
@ -26,25 +21,45 @@
</listitem>
<listitem>
<para>
table per concrete class (some limitations)
table per concrete class
</para>
</listitem>
</itemizedlist>
<para>
It is even possible to use different mapping strategies for different
branches of the same inheritance hierarchy, but the same limitations
apply as apply to table-per-concrete class mappings. Hibernate does
not support mixing <literal>&lt;subclass&gt;</literal> mappings and
<literal>&lt;joined-subclass&gt;</literal> mappings inside the same
<literal>&lt;class&gt;</literal> element. However, it is possible to
use a <literal>&lt;join&gt;</literal> element to map this.
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>&lt;subclass&gt;</literal>,
and <literal>&lt;joined-subclass&gt;</literal> and
<literal>&lt;union-subclass&gt;</literal> mappings under the same root
<literal>&lt;class&gt;</literal> element. It is possible to mix together
the table per hierarchy and table per subclass strategies, under the
the same <literal>&lt;class&gt;</literal> element, by combining the
<literal>&lt;subclass&gt;</literal> and <literal>&lt;join&gt;</literal>
elements (see below).
</para>
<sect2>
<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
<literal>ChequePayment</literal>. The table per hierarchy mapping would
look like:
</para>
@ -56,6 +71,7 @@
<property name="amount" column="AMOUNT"/>
...
<subclass name="CreditCardPayment" discriminator-value="CREDIT">
<property name="creditCardType" column="CCTYPE"/>
...
</subclass>
<subclass name="CashPayment" discriminator-value="CASH">
@ -67,13 +83,18 @@
</class>]]></programlisting>
<para>
Exactly one table is required. There is one big limitation of this
mapping strategy: columns declared by the subclasses may not have
<literal>NOT NULL</literal> constraints.
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>
<title>Table per class subclass</title>
<para>
A table-per-subclass mapping would look like:
A table per subclass mapping would look like:
</para>
<programlisting><![CDATA[<class name="Payment" table="PAYMENT">
@ -84,6 +105,7 @@
...
<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">
@ -102,34 +124,140 @@
is actually a one-to-one association).
</para>
</sect2>
<sect2>
<title>Table per subclass, using a discriminator</title>
<para>
Note that Hibernate's implementation of table-per-subclass requires
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
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.
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>&lt;subclass&gt;</literal> and
<literal>&lt;join&gt;</literal>, as follow:
</para>
<para>
TODO: document usage of join for discriminators in table-per-subclass
</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">
<join table="CASH_PAYMENT">
...
</join>
</subclass>
<subclass name="ChequePayment" discriminator-value="CHEQUE">
<join table="CHEQUE_PAYMENT" fetch="select">
...
</join>
</subclass>
</class>]]></programlisting>
<para>
TODO: document usage of join for mixing inheritance mapping strategies
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>
<title>Mixing table per class hierarchy with table per subclass</title>
<para>
For either of these two mapping strategies, a polymorphic
association to <literal>Payment</literal> is mapped using
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>&lt;many-to-one&gt;</literal>.
</para>
<programlisting><![CDATA[<many-to-one name="payment"
column="PAYMENT"
class="Payment"/>]]></programlisting>
<programlisting><![CDATA[<many-to-one name="payment" column="PAYMENT_ID" class="Payment"/>]]></programlisting>
<para>The table-per-concrete-class strategy is very different.</para>
</sect2>
<sect2>
<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>&lt;union-subclass&gt;</literal>.
</para>
<programlisting><![CDATA[<class name="Payment">
<id name="id" type="long" column="PAYMENT_ID">
<generator class="native"/>
</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. 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.)
</para>
</sect2>
<sect2>
<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">
@ -156,48 +284,40 @@
</class>]]></programlisting>
<para>
Three tables were required. Notice that nowhere do we
mention the <literal>Payment</literal> interface explicitly.
Instead, we make use of Hibernate's <emphasis>implicit
polymorphism</emphasis>. Also notice that properties of
<literal>Payment</literal> are mapped in each of the
subclasses.
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.
</para>
<para>
In this case, a polymorphic association to <literal>Payment</literal>
is mapped using <literal>&lt;any&gt;</literal>.
The disadvantage of this approach is that Hibernate does not generate SQL
<literal>UNION</literal>s when performing polymorphic queries.
</para>
<programlisting><![CDATA[<any name="payment"
meta-type="class"
id-type="long">
<para>
For this mapping strategy, a polymorphic association to <literal>Payment</literal>
is usually mapped using <literal>&lt;any&gt;</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>
<para>
It would be better if we defined a <literal>UserType</literal>
as the <literal>meta-type</literal>, to handle the mapping from
type discriminator strings to <literal>Payment</literal> subclass.
</para>
</sect2>
<programlisting><![CDATA[<any name="payment"
meta-type="PaymentMetaType"
id-type="long">
<column name="PAYMENT_TYPE"/> <!-- CREDIT, CASH or CHEQUE -->
<column name="PAYMENT_ID"/>
</any>]]></programlisting>
<sect2>
<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>&lt;class&gt;</literal> element (and since
<literal>Payment</literal> is just an interface), each of
the subclasses could easily be part of another table-per-class
or table-per-subclass inheritance hierarchy! (And you can
still use polymorphic queries against the
<literal>Payment</literal> interface.)
There is one further thing to notice about this mapping. Since the subclasses
are each mapped in their own <literal>&lt;class&gt;</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">
@ -238,9 +358,7 @@
not instances of <literal>NonelectronicTransaction</literal>.
</para>
<para>
TODO: Document union-subclass for polymorphic-table-per-concrete-class mappings
</para>
</sect2>
</sect1>
@ -249,13 +367,13 @@
<para>
There are certain limitations to the "implicit polymorphism" approach to
the table-per-concrete-class mapping strategy. There are somewhat less
the table per concrete-class mapping strategy. There are somewhat less
restrictive limitations to <literal>&lt;union-subclass&gt;</literal>
mappings. (TODO)
mappings.
</para>
<para>
The following table shows the limitations of table-per-concrete-class
The following table shows the limitations of table per concrete-class
mappings, and of implicit polymorphism, in Hibernate.
</para>
@ -285,7 +403,7 @@
</thead>
<tbody>
<row>
<entry>table-per-class-hierarchy</entry>
<entry>table per class-hierarchy</entry>
<entry><literal>&lt;many-to-one&gt;</literal></entry>
<entry><literal>&lt;one-to-one&gt;</literal></entry>
<entry><literal>&lt;one-to-many&gt;</literal></entry>
@ -296,7 +414,7 @@
<entry><emphasis>supported</emphasis></entry>
</row>
<row>
<entry>table-per-subclass</entry>
<entry>table per subclass</entry>
<entry><literal>&lt;many-to-one&gt;</literal></entry>
<entry><literal>&lt;one-to-one&gt;</literal></entry>
<entry><literal>&lt;one-to-many&gt;</literal></entry>
@ -307,18 +425,7 @@
<entry><emphasis>supported</emphasis></entry>
</row>
<row>
<entry>table-per-concrete-class (implicit polymorphism)</entry>
<entry><literal>&lt;any&gt;</literal></entry>
<entry><emphasis>not supported</emphasis></entry>
<entry><emphasis>not supported</emphasis></entry>
<entry><literal>&lt;many-to-any&gt;</literal></entry>
<entry><emphasis>use a query</emphasis></entry>
<entry><literal>from Payment p</literal></entry>
<entry><emphasis>not supported</emphasis></entry>
<entry><emphasis>not supported</emphasis></entry>
</row>
<row>
<entry>table-per-concrete-class (union-subclass)</entry>
<entry>table per concrete-class (union-subclass)</entry>
<entry><literal>&lt;many-to-one&gt;</literal></entry>
<entry><literal>&lt;one-to-one&gt;</literal></entry>
<entry><literal>&lt;one-to-many&gt;</literal> (for <literal>inverse="true"</literal> only)</entry>
@ -328,6 +435,17 @@
<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>&lt;any&gt;</literal></entry>
<entry><emphasis>not supported</emphasis></entry>
<entry><emphasis>not supported</emphasis></entry>
<entry><literal>&lt;many-to-any&gt;</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>