403 lines
15 KiB
XML
403 lines
15 KiB
XML
<chapter id="components">
|
|
<title>Component Mapping</title>
|
|
|
|
<para>
|
|
The notion of a <emphasis>component</emphasis> is re-used in several different contexts,
|
|
for different purposes, throughout Hibernate.
|
|
</para>
|
|
|
|
<sect1 id="components-dependentobjects">
|
|
<title>Dependent objects</title>
|
|
|
|
<para>
|
|
A component is a contained object that is persisted as a value type, not an entity
|
|
reference. The term "component" refers to the object-oriented notion of composition
|
|
(not to architecture-level components). For example, you might model a person like this:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[public class Person {
|
|
private java.util.Date birthday;
|
|
private Name name;
|
|
private String key;
|
|
public String getKey() {
|
|
return key;
|
|
}
|
|
private void setKey(String key) {
|
|
this.key=key;
|
|
}
|
|
public java.util.Date getBirthday() {
|
|
return birthday;
|
|
}
|
|
public void setBirthday(java.util.Date birthday) {
|
|
this.birthday = birthday;
|
|
}
|
|
public Name getName() {
|
|
return name;
|
|
}
|
|
public void setName(Name name) {
|
|
this.name = name;
|
|
}
|
|
......
|
|
......
|
|
}]]></programlisting>
|
|
|
|
<programlisting><![CDATA[public class Name {
|
|
char initial;
|
|
String first;
|
|
String last;
|
|
public String getFirst() {
|
|
return first;
|
|
}
|
|
void setFirst(String first) {
|
|
this.first = first;
|
|
}
|
|
public String getLast() {
|
|
return last;
|
|
}
|
|
void setLast(String last) {
|
|
this.last = last;
|
|
}
|
|
public char getInitial() {
|
|
return initial;
|
|
}
|
|
void setInitial(char initial) {
|
|
this.initial = initial;
|
|
}
|
|
}]]></programlisting>
|
|
|
|
<para>
|
|
Now <literal>Name</literal> may be persisted as a component of
|
|
<literal>Person</literal>. Notice that <literal>Name</literal> defines getter
|
|
and setter methods for its persistent properties, but doesn't need to declare
|
|
any interfaces or identifier properties.
|
|
</para>
|
|
|
|
<para>
|
|
Our Hibernate mapping would look like:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[<class name="eg.Person" table="person">
|
|
<id name="Key" column="pid" type="string">
|
|
<generator class="uuid.hex"/>
|
|
</id>
|
|
<property name="birthday" type="date"/>
|
|
<component name="Name" class="eg.Name"> <!-- class attribute optional -->
|
|
<property name="initial"/>
|
|
<property name="first"/>
|
|
<property name="last"/>
|
|
</component>
|
|
</class>]]></programlisting>
|
|
|
|
<para>
|
|
The person table would have the columns <literal>pid</literal>,
|
|
<literal>birthday</literal>,
|
|
<literal>initial</literal>,
|
|
<literal>first</literal> and
|
|
<literal>last</literal>.
|
|
</para>
|
|
|
|
<para>
|
|
Like all value types, components do not support shared references. In other words, two
|
|
persons could have the same name, but the two person objects would contain two independent
|
|
name ojects, only "the same" by value. The null value semantics of a component are
|
|
<emphasis>ad hoc</emphasis>. When reloading the containing object, Hibernate will assume
|
|
that if all component columns are null, then the entire component is null. This should
|
|
be okay for most purposes.
|
|
</para>
|
|
|
|
<para>
|
|
The properties of a component may be of any Hibernate type (collections, many-to-one
|
|
associations, other components, etc). Nested components should <emphasis>not</emphasis>
|
|
be considered an exotic usage. Hibernate is intended to support a very fine-grained
|
|
object model.
|
|
</para>
|
|
|
|
<para>
|
|
The <literal><component></literal> element allows a <literal><parent></literal>
|
|
subelement that maps a property of the component class as a reference back to the
|
|
containing entity.
|
|
</para>
|
|
|
|
<programlisting><![CDATA[<class name="eg.Person" table="person">
|
|
<id name="Key" column="pid" type="string">
|
|
<generator class="uuid.hex"/>
|
|
</id>
|
|
<property name="birthday" type="date"/>
|
|
<component name="Name" class="eg.Name">
|
|
<parent name="namedPerson"/> <!-- reference back to the Person -->
|
|
<property name="initial"/>
|
|
<property name="first"/>
|
|
<property name="last"/>
|
|
</component>
|
|
</class>]]></programlisting>
|
|
|
|
</sect1>
|
|
|
|
<sect1 id="components-incollections" revision="1">
|
|
<title>Collections of dependent objects</title>
|
|
|
|
<para>
|
|
Collections of components are supported (eg. an array of type
|
|
<literal>Name</literal>). Declare your component collection by
|
|
replacing the <literal><element></literal> tag with a
|
|
<literal><composite-element></literal> tag.
|
|
</para>
|
|
|
|
<programlisting><![CDATA[<set name="someNames" table="some_names" lazy="true">
|
|
<key column="id"/>
|
|
<composite-element class="eg.Name"> <!-- class attribute required -->
|
|
<property name="initial"/>
|
|
<property name="first"/>
|
|
<property name="last"/>
|
|
</composite-element>
|
|
</set>]]></programlisting>
|
|
|
|
<para>
|
|
Note: if you define a <literal>Set</literal> of composite elements, it is
|
|
very important to implement <literal>equals()</literal> and
|
|
<literal>hashCode()</literal> correctly.
|
|
</para>
|
|
|
|
<para>
|
|
Composite elements may contain components but not collections. If your
|
|
composite element itself contains
|
|
components, use the <literal><nested-composite-element></literal>
|
|
tag. This is a pretty exotic case - a collection of components which
|
|
themselves have components. By this stage you should be asking yourself
|
|
if a one-to-many association is more appropriate. Try remodelling the
|
|
composite element as an entity - but note that even though the Java model
|
|
is the same, the relational model and persistence semantics are still
|
|
slightly different.
|
|
</para>
|
|
|
|
<para>
|
|
Please note that a composite element mapping doesn't support null-able properties
|
|
if you're using a <literal><set></literal>. Hibernate
|
|
has to use each columns value to identify a record when deleting objects
|
|
(there is no separate primary key column in the composite element table),
|
|
which is not possible with null values. You have to either use only
|
|
not-null properties in a composite-element or choose a
|
|
<literal><list></literal>, <literal><map></literal>,
|
|
<literal><bag></literal> or <literal><idbag></literal>.
|
|
</para>
|
|
|
|
<para>
|
|
A special case of a composite element is a composite element with a nested
|
|
<literal><many-to-one></literal> element. A mapping like this allows
|
|
you to map extra columns of a many-to-many association table to the
|
|
composite element class. The following is a many-to-many association
|
|
from <literal>Order</literal> to <literal>Item</literal> where
|
|
<literal>purchaseDate</literal>, <literal>price</literal> and
|
|
<literal>quantity</literal> are properties of the association:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[<class name="eg.Order" .... >
|
|
....
|
|
<set name="purchasedItems" table="purchase_items" lazy="true">
|
|
<key column="order_id">
|
|
<composite-element class="eg.Purchase">
|
|
<property name="purchaseDate"/>
|
|
<property name="price"/>
|
|
<property name="quantity"/>
|
|
<many-to-one name="item" class="eg.Item"/> <!-- class attribute is optional -->
|
|
</composite-element>
|
|
</set>
|
|
</class>]]></programlisting>
|
|
|
|
<para>
|
|
Of course, there can't be a reference to the purchae on the other side, for
|
|
bidirectional association navigation. Remember that components are value types and
|
|
don't allow shared references. A single <literal>Purchase</literal> can be in the
|
|
set of an <literal>Order</literal>, but it can't be referenced by the <literal>Item</literal>
|
|
at the same time.
|
|
</para>
|
|
|
|
<para>Even ternary (or quaternary, etc) associations are possible:</para>
|
|
|
|
<programlisting><![CDATA[<class name="eg.Order" .... >
|
|
....
|
|
<set name="purchasedItems" table="purchase_items" lazy="true">
|
|
<key column="order_id">
|
|
<composite-element class="eg.OrderLine">
|
|
<many-to-one name="purchaseDetails class="eg.Purchase"/>
|
|
<many-to-one name="item" class="eg.Item"/>
|
|
</composite-element>
|
|
</set>
|
|
</class>]]></programlisting>
|
|
|
|
<para>
|
|
Composite elements may appear in queries using the same syntax as
|
|
associations to other entities.
|
|
</para>
|
|
|
|
</sect1>
|
|
|
|
<sect1 id="components-asmapindex">
|
|
<title>Components as Map indices</title>
|
|
|
|
<para>
|
|
The <literal><composite-map-key></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.
|
|
</para>
|
|
</sect1>
|
|
|
|
<sect1 id="components-compositeid" revision="1">
|
|
<title>Components as composite identifiers</title>
|
|
|
|
<para>
|
|
You may use a component as an identifier of an entity class. Your component
|
|
class must satisfy certain requirements:
|
|
</para>
|
|
|
|
<itemizedlist spacing="compact">
|
|
<listitem>
|
|
<para>
|
|
It must implement <literal>java.io.Serializable</literal>.
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
It must re-implement <literal>equals()</literal> and
|
|
<literal>hashCode()</literal>, consistently with the database's
|
|
notion of composite key equality.
|
|
</para>
|
|
</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>
|
|
Use the <literal><composite-id></literal> tag (with nested
|
|
<literal><key-property></literal> elements) in place of the usual
|
|
<literal><id></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="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 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="orderLine" class="OrderLine">
|
|
<!-- the "class" attribute is optional, as usual -->
|
|
<column name="lineId"/>
|
|
<column name="orderId"/>
|
|
<column name="customerId"/>
|
|
</many-to-one>]]></programlisting>
|
|
|
|
<para>
|
|
(Note that the <literal><column></literal> tag is an alternative to the
|
|
<literal>column</literal> attribute everywhere.)
|
|
</para>
|
|
|
|
<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>
|
|
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><one-to-many></literal> element, as usual, declares no columns.)
|
|
</para>
|
|
|
|
<para>
|
|
If <literal>OrderLine</literal> itself owns a collection, it also has a composite
|
|
foreign key.
|
|
</para>
|
|
|
|
<programlisting><![CDATA[<class name="OrderLine">
|
|
....
|
|
....
|
|
<list name="deliveryAttempts">
|
|
<key> <!-- a collection inherits the composite key type -->
|
|
<column name="lineId"/>
|
|
<column name="orderId"/>
|
|
<column name="customerId"/>
|
|
</key>
|
|
<list-index column="attemptId" base="1"/>
|
|
<composite-element class="DeliveryAttempt">
|
|
...
|
|
</composite-element>
|
|
</set>
|
|
</class>]]></programlisting>
|
|
|
|
</sect1>
|
|
|
|
<sect1 id="components-dynamic" revision="1">
|
|
<title>Dynamic components</title>
|
|
|
|
<para>
|
|
You may even map a property of type <literal>Map</literal>:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[<dynamic-component name="userAttributes">
|
|
<property name="foo" column="FOO"/>
|
|
<property name="bar" column="BAR"/>
|
|
<many-to-one name="baz" class="Baz" column="BAZ_ID"/>
|
|
</dynamic-component>]]></programlisting>
|
|
|
|
<para>
|
|
The semantics of a <literal><dynamic-component></literal> mapping are identical
|
|
to <literal><component></literal>. The advantage of this kind of mapping is
|
|
the ability to determine the actual properties of the bean at deployment time, just
|
|
by editing the mapping document. Runtime manipulation of the mapping document is
|
|
also possible, using a DOM parser. Even better, you can access (and change) Hibernate's
|
|
configuration-time metamodel via the <literal>Configuration</literal> object.
|
|
</para>
|
|
|
|
</sect1>
|
|
|
|
</chapter>
|