374 lines
14 KiB
XML
374 lines
14 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.
|
|
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. 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">
|
|
<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>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-index></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">
|
|
<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>
|
|
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 instances and instances saved in a previous session.
|
|
</para>
|
|
|
|
<para>
|
|
You may instead implement <literal>Interceptor.isUnsaved()</literal> if
|
|
you wish to use <literal>saveOrUpdate()</literal> or cascading save / update.
|
|
As an alternative, you may also set the <literal>unsaved-value</literal>
|
|
attribute on a <literal><version></literal> (or
|
|
<literal><timestamp></literal>) element to specify a
|
|
value that indicates a new transient instance. In this case, the version
|
|
of the entity is used instead of the (assigned) identifier and you don't have
|
|
to implement <literal>Interceptor.isUnsaved()</literal> yourself.
|
|
</para>
|
|
|
|
<para>
|
|
Use the <literal><composite-id></literal> tag (same attributes and
|
|
elements as <literal><component></literal>) in place of
|
|
<literal><id></literal> for the declaration of a composite identifier
|
|
class:
|
|
</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"/>
|
|
</composite-id>
|
|
<property name="name"/>
|
|
....
|
|
</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:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[<many-to-one name="foo" class="eg.Foo">
|
|
<!-- the "class" attribute is optional, as usual -->
|
|
<column name="foo_string"/>
|
|
<column name="foo_short"/>
|
|
<column name="foo_date"/>
|
|
</many-to-one>]]></programlisting>
|
|
|
|
<para>
|
|
This new <literal><column></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:
|
|
</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"/>
|
|
</many-to-many>
|
|
</set>]]></programlisting>
|
|
|
|
<para>
|
|
On the other hand, <literal><one-to-many></literal>, as usual, declares no columns.
|
|
</para>
|
|
|
|
<para>
|
|
If <literal>Foo</literal> itself contains collections, they will also need a
|
|
composite foreign key.
|
|
</para>
|
|
|
|
<programlisting><![CDATA[<class name="eg.Foo">
|
|
....
|
|
....
|
|
<set name="dates" lazy="true">
|
|
<key> <!-- a collection inherits the composite key type -->
|
|
<column name="foo_string"/>
|
|
<column name="foo_short"/>
|
|
<column name="foo_date"/>
|
|
</key>
|
|
<element column="foo_date" type="date"/>
|
|
</set>
|
|
</class>]]></programlisting>
|
|
|
|
</sect1>
|
|
|
|
<sect1 id="components-dynamic">
|
|
<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="eg.Baz" column="BAZ"/>
|
|
</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.)
|
|
</para>
|
|
|
|
</sect1>
|
|
|
|
</chapter>
|