1146 lines
46 KiB
XML
1146 lines
46 KiB
XML
<chapter id="collections">
|
|
<title>Collection Mapping</title>
|
|
|
|
<sect1 id="collections-persistent" revision="3">
|
|
<title>Persistent collections</title>
|
|
|
|
<para>
|
|
Hibernate requires that persistent collection-valued fields be declared
|
|
as an interface type, for example:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[public class Product {
|
|
private String serialNumber;
|
|
private Set parts = new HashSet();
|
|
|
|
public Set getParts() { return parts; }
|
|
void setParts(Set parts) { this.parts = parts; }
|
|
public String getSerialNumber() { return serialNumber; }
|
|
void setSerialNumber(String sn) { serialNumber = sn; }
|
|
}]]></programlisting>
|
|
|
|
<para>
|
|
The actual interface might be <literal>java.util.Set</literal>,
|
|
<literal>java.util.Collection</literal>, <literal>java.util.List</literal>,
|
|
<literal>java.util.Map</literal>, <literal>java.util.SortedSet</literal>,
|
|
<literal>java.util.SortedMap</literal> or ... anything you like! (Where
|
|
"anything you like" means you will have to write an implementation of
|
|
<literal>org.hibernate.usertype.UserCollectionType</literal>.)
|
|
</para>
|
|
|
|
<para>
|
|
Notice how we initialized the instance variable with an instance of
|
|
<literal>HashSet</literal>. This is the best way to initialize collection
|
|
valued properties of newly instantiated (non-persistent) instances. When
|
|
you make the instance persistent - by calling <literal>persist()</literal>,
|
|
for example - Hibernate will actually replace the <literal>HashSet</literal>
|
|
with an instance of Hibernate's own implementation of <literal>Set</literal>.
|
|
Watch out for errors like this:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[Cat cat = new DomesticCat();
|
|
Cat kitten = new DomesticCat();
|
|
....
|
|
Set kittens = new HashSet();
|
|
kittens.add(kitten);
|
|
cat.setKittens(kittens);
|
|
session.persist(cat);
|
|
kittens = cat.getKittens(); // Okay, kittens collection is a Set
|
|
(HashSet) cat.getKittens(); // Error!]]></programlisting>
|
|
|
|
<para>
|
|
The persistent collections injected by Hibernate behave like
|
|
<literal>HashMap</literal>, <literal>HashSet</literal>,
|
|
<literal>TreeMap</literal>, <literal>TreeSet</literal> or
|
|
<literal>ArrayList</literal>, depending upon the interface type.
|
|
</para>
|
|
|
|
<para>
|
|
Collections instances have the usual behavior of value types. They are
|
|
automatically persisted when referenced by a persistent object and
|
|
automatically deleted when unreferenced. If a collection is passed from one
|
|
persistent object to another, its elements might be moved from one table to
|
|
another. Two entities may not share a reference to the same collection
|
|
instance. Due to the underlying relational model, collection-valued properties
|
|
do not support null value semantics; Hibernate does not distinguish between
|
|
a null collection reference and an empty collection.
|
|
</para>
|
|
|
|
<para>
|
|
You shouldn't have to worry much about any of this. Use persistent collections
|
|
the same way you use ordinary Java collections. Just make sure you understand
|
|
the semantics of bidirectional associations (discussed later).
|
|
</para>
|
|
|
|
</sect1>
|
|
|
|
<sect1 id="collections-mapping" revision="2">
|
|
<title>Collection mappings</title>
|
|
|
|
<para>
|
|
The Hibernate mapping element used for mapping a collection depends upon
|
|
the type of the interface. For example, a <literal><set></literal>
|
|
element is used for mapping properties of type <literal>Set</literal>.
|
|
</para>
|
|
|
|
<programlisting><![CDATA[<class name="Product">
|
|
<id name="serialNumber" column="productSerialNumber"/>
|
|
<set name="parts">
|
|
<key column="productSerialNumber" not-null="true"/>
|
|
<one-to-many class="Part"/>
|
|
</set>
|
|
</class>]]></programlisting>
|
|
|
|
<para>
|
|
Apart from <literal><set></literal>, there is also
|
|
<literal><list></literal>, <literal><map></literal>,
|
|
<literal><bag></literal>, <literal><array></literal> and
|
|
<literal><primitive-array></literal> mapping elements. The
|
|
<literal><map></literal> element is representative:
|
|
</para>
|
|
|
|
<programlistingco>
|
|
<areaspec>
|
|
<area id="mappingcollection1" coords="2 65"/>
|
|
<area id="mappingcollection2" coords="3 65"/>
|
|
<area id="mappingcollection3" coords="4 65"/>
|
|
<area id="mappingcollection4" coords="5 65"/>
|
|
<area id="mappingcollection5" coords="6 65"/>
|
|
<area id="mappingcollection6" coords="7 65"/>
|
|
<area id="mappingcollection7" coords="8 65"/>
|
|
<area id="mappingcollection8" coords="9 65"/>
|
|
<area id="mappingcollection9" coords="10 65"/>
|
|
<area id="mappingcollection10" coords="11 65"/>
|
|
<area id="mappingcollection11" coords="12 65"/>
|
|
<area id="mappingcollection12" coords="13 65"/>
|
|
<area id="mappingcollection13" coords="14 65"/>
|
|
</areaspec>
|
|
<programlisting><![CDATA[<map
|
|
name="propertyName"
|
|
table="table_name"
|
|
schema="schema_name"
|
|
lazy="true|false"
|
|
inverse="true|false"
|
|
cascade="all|none|save-update|delete|all-delete-orphan"
|
|
sort="unsorted|natural|comparatorClass"
|
|
order-by="column_name asc|desc"
|
|
where="arbitrary sql where condition"
|
|
fetch="join|select|subselect"
|
|
batch-size="N"
|
|
access="field|property|ClassName"
|
|
optimistic-lock="true|false"
|
|
node="element-name|."
|
|
embed-xml="true|false"
|
|
>
|
|
|
|
<key .... />
|
|
<map-key .... />
|
|
<element .... />
|
|
</map>]]></programlisting>
|
|
<calloutlist>
|
|
<callout arearefs="mappingcollection1">
|
|
<para>
|
|
<literal>name</literal> the collection property name
|
|
</para>
|
|
</callout>
|
|
<callout arearefs="mappingcollection2">
|
|
<para>
|
|
<literal>table</literal> (optional - defaults to property name) the
|
|
name of the collection table (not used for one-to-many associations)
|
|
</para>
|
|
</callout>
|
|
<callout arearefs="mappingcollection3">
|
|
<para>
|
|
<literal>schema</literal> (optional) the name of a table schema to
|
|
override the schema declared on the root element
|
|
</para>
|
|
</callout>
|
|
<callout arearefs="mappingcollection4">
|
|
<para>
|
|
<literal>lazy</literal> (optional - defaults to <literal>true</literal>)
|
|
may be used to disable lazy fetching and specify that the association is
|
|
always eagerly fetched (not available for arrays)
|
|
</para>
|
|
</callout>
|
|
<callout arearefs="mappingcollection5">
|
|
<para>
|
|
<literal>inverse</literal> (optional - defaults to <literal>false</literal>)
|
|
mark this collection as the "inverse" end of a bidirectional association
|
|
</para>
|
|
</callout>
|
|
<callout arearefs="mappingcollection6">
|
|
<para>
|
|
<literal>cascade</literal> (optional - defaults to <literal>none</literal>)
|
|
enable operations to cascade to child entities
|
|
</para>
|
|
</callout>
|
|
<callout arearefs="mappingcollection7">
|
|
<para>
|
|
<literal>sort</literal> (optional) specify a sorted collection with
|
|
<literal>natural</literal> sort order, or a given comparator class
|
|
</para>
|
|
</callout>
|
|
<callout arearefs="mappingcollection8">
|
|
<para>
|
|
<literal>order-by</literal> (optional, JDK1.4 only) specify a table column (or columns)
|
|
that define the iteration order of the <literal>Map</literal>, <literal>Set</literal>
|
|
or bag, together with an optional <literal>asc</literal> or <literal>desc</literal>
|
|
</para>
|
|
</callout>
|
|
<callout arearefs="mappingcollection9">
|
|
<para>
|
|
<literal>where</literal> (optional) specify an arbitrary SQL <literal>WHERE</literal>
|
|
condition to be used when retrieving or removing the collection (useful if the
|
|
collection should contain only a subset of the available data)
|
|
</para>
|
|
</callout>
|
|
<callout arearefs="mappingcollection10">
|
|
<para>
|
|
<literal>fetch</literal> (optional, defaults to <literal>select</literal>) Choose
|
|
between outer-join fetching, fetching by sequential select, and fetching by sequential
|
|
subselect.
|
|
</para>
|
|
</callout>
|
|
<callout arearefs="mappingcollection11">
|
|
<para>
|
|
<literal>batch-size</literal> (optional, defaults to <literal>1</literal>) specify a
|
|
"batch size" for lazily fetching instances of this collection.
|
|
</para>
|
|
</callout>
|
|
<callout arearefs="mappingcollection12">
|
|
<para>
|
|
<literal>access</literal> (optional - defaults to <literal>property</literal>): The
|
|
strategy Hibernate should use for accessing the property value.
|
|
</para>
|
|
</callout>
|
|
<callout arearefs="mappingcollection12">
|
|
<para>
|
|
<literal>optimistic-lock</literal> (optional - defaults to <literal>true</literal>):
|
|
Species that changes to the state of the collection results in increment of the
|
|
owning entity's version. (For one to many associations, it is often reasonable to
|
|
disable this setting.)
|
|
</para>
|
|
</callout>
|
|
</calloutlist>
|
|
</programlistingco>
|
|
|
|
<sect2 id="collections-foreignkeys" >
|
|
<title>Collection foreign keys</title>
|
|
|
|
<para>
|
|
Collection instances are distinguished in the database by the foreign key of
|
|
the entity that owns the collection. This foreign key is referred to as the
|
|
<emphasis>collection key column</emphasis> (or columns) of the collection
|
|
table. The collection key column is mapped by the <literal><key></literal>
|
|
element.
|
|
</para>
|
|
|
|
<para>
|
|
There may be a nullability constraint on the foreign key column. For most
|
|
collections, this is implied. For unidirectional one to many associations,
|
|
the foreign key column is nullable by default, so you might need to specify
|
|
<literal>not-null="true"</literal>.
|
|
</para>
|
|
|
|
<programlisting><![CDATA[<key column="productSerialNumber" not-null="true"/>]]></programlisting>
|
|
|
|
<para>
|
|
The foreign key constraint may use <literal>ON DELETE CASCADE</literal>.
|
|
</para>
|
|
|
|
<programlisting><![CDATA[<key column="productSerialNumber" on-delete="cascade"/>]]></programlisting>
|
|
|
|
<para>
|
|
See the previous chapter for a full definition of the <literal><key></literal>
|
|
element.
|
|
</para>
|
|
|
|
</sect2>
|
|
|
|
<sect2 id="collections-elements" >
|
|
<title>Collection elements</title>
|
|
|
|
<para>
|
|
Collections may contain almost any other Hibernate type, including all basic types,
|
|
custom types, components, and of course, references to other entities. This is an
|
|
important distinction: an object in a collection might be handled with "value"
|
|
semantics (its lifecycle fully depends on the collection owner) or it might be a
|
|
reference to another entity, with its own lifecycle. In the latter case, only the
|
|
"link" between the two objects is considered to be state held by the collection.
|
|
</para>
|
|
|
|
<para>
|
|
The contained type is referred to as the <emphasis>collection element type</emphasis>.
|
|
Collection elements are mapped by <literal><element></literal> or
|
|
<literal><composite-element></literal>, or in the case of entity references,
|
|
with <literal><one-to-many></literal> or <literal><many-to-many></literal>.
|
|
The first two map elements with value semantics, the next two are used to map entity
|
|
associations.
|
|
</para>
|
|
|
|
</sect2>
|
|
|
|
<sect2 id="collections-indexed">
|
|
<title>Indexed collections</title>
|
|
|
|
<para>
|
|
All collection mappings, except those with set and bag semantics, need an
|
|
<emphasis>index column</emphasis> in the collection table - a column that maps to an
|
|
array index, or <literal>List</literal> index, or <literal>Map</literal> key. The
|
|
index of a <literal>Map</literal> may be of any basic type, mapped with
|
|
<literal><map-key></literal>, it may be an entity reference mapped with
|
|
<literal><map-key-many-to-many></literal>, or it may be a composite type,
|
|
mapped with <literal><composite-map-key></literal>. The index of an array or
|
|
list is always of type <literal>integer</literal> and is mapped using the
|
|
<literal><list-index></literal> element. The mapped column contains
|
|
sequential integers (numbered from zero, by default).
|
|
</para>
|
|
|
|
<programlistingco>
|
|
<areaspec>
|
|
<area id="index1" coords="2 45"/>
|
|
<area id="index2" coords="3 45"/>
|
|
</areaspec>
|
|
<programlisting><![CDATA[<list-index
|
|
column="column_name"
|
|
base="0|1|..."/>]]></programlisting>
|
|
<calloutlist>
|
|
<callout arearefs="index1">
|
|
<para>
|
|
<literal>column_name</literal> (required): The name of the column holding the
|
|
collection index values.
|
|
</para>
|
|
</callout>
|
|
<callout arearefs="index1">
|
|
<para>
|
|
<literal>base</literal> (optional, defaults to <literal>0</literal>): The value
|
|
of the index column that corresponds to the first element of the list or array.
|
|
</para>
|
|
</callout>
|
|
</calloutlist>
|
|
</programlistingco>
|
|
|
|
<programlistingco>
|
|
<areaspec>
|
|
<area id="mapkey1" coords="2 45"/>
|
|
<area id="mapkey2" coords="3 45"/>
|
|
<area id="mapkey3" coords="4 45"/>
|
|
</areaspec>
|
|
<programlisting><![CDATA[<map-key
|
|
column="column_name"
|
|
formula="any SQL expression"
|
|
type="type_name"
|
|
node="@attribute-name"
|
|
length="N"/>]]></programlisting>
|
|
<calloutlist>
|
|
<callout arearefs="mapkey1">
|
|
<para>
|
|
<literal>column</literal> (optional): The name of the column holding the
|
|
collection index values.
|
|
</para>
|
|
</callout>
|
|
<callout arearefs="mapkey2">
|
|
<para>
|
|
<literal>formula</literal> (optional): A SQL formula used to evaluate the
|
|
key of the map.
|
|
</para>
|
|
</callout>
|
|
<callout arearefs="mapkey3">
|
|
<para>
|
|
<literal>type</literal> (optional, defaults to <literal>integer</literal>):
|
|
The type of the collection index.
|
|
</para>
|
|
</callout>
|
|
</calloutlist>
|
|
</programlistingco>
|
|
|
|
<programlistingco>
|
|
<areaspec>
|
|
<area id="indexmanytomany1" coords="2 45"/>
|
|
<area id="indexmanytomany2" coords="3 45"/>
|
|
<area id="indexmanytomany3" coords="3 45"/>
|
|
</areaspec>
|
|
<programlisting><![CDATA[<map-key-many-to-many
|
|
column="column_name"
|
|
formula="any SQL expression"
|
|
class="ClassName"
|
|
/>]]></programlisting>
|
|
<calloutlist>
|
|
<callout arearefs="indexmanytomany1">
|
|
<para>
|
|
<literal>column</literal> (optional): The name of the foreign key
|
|
column for the collection index values.
|
|
</para>
|
|
</callout>
|
|
<callout arearefs="indexmanytomany2">
|
|
<para>
|
|
<literal>formula</literal> (optional): A SQL formula used to evaluate the
|
|
foreign key of the map key.
|
|
</para>
|
|
</callout>
|
|
<callout arearefs="indexmanytomany3">
|
|
<para>
|
|
<literal>class</literal> (required): The entity class used as the
|
|
collection index.
|
|
</para>
|
|
</callout>
|
|
</calloutlist>
|
|
</programlistingco>
|
|
|
|
|
|
<para>
|
|
If your table doesn't have an index column, and you still wish to use <literal>List</literal>
|
|
as the property type, you should map the property as a Hibernate <emphasis><bag></emphasis>.
|
|
A bag does not retain its order when it is retrieved from the database, but it may be
|
|
optionally sorted or ordered.
|
|
</para>
|
|
|
|
</sect2>
|
|
|
|
<para>
|
|
There are quite a range of mappings that can be generated for collections, covering
|
|
many common relational models. We suggest you experiment with the schema generation tool
|
|
to get a feeling for how various mapping declarations translate to database tables.
|
|
</para>
|
|
|
|
<sect2 id="collections-ofvalues" revision="1">
|
|
<title>Collections of values and many-to-many associations</title>
|
|
|
|
<para>
|
|
Any collection of values or many-to-many association requires a dedicated
|
|
<emphasis>collection table</emphasis> with a foreign key column or columns,
|
|
<emphasis>collection element column</emphasis> or columns and possibly
|
|
an index column or columns.
|
|
</para>
|
|
|
|
<para>
|
|
For a collection of values, we use the <literal><element></literal> tag.
|
|
</para>
|
|
|
|
<programlistingco>
|
|
<areaspec>
|
|
<area id="element1b" coords="2 50"/>
|
|
<area id="element2b" coords="3 50"/>
|
|
<area id="element3b" coords="4 50"/>
|
|
</areaspec>
|
|
<programlisting><![CDATA[<element
|
|
column="column_name"
|
|
formula="any SQL expression"
|
|
type="typename"
|
|
length="N"
|
|
precision="N"
|
|
scale="N"
|
|
not-null="true|false"
|
|
unique="true|false"
|
|
node="element-name"
|
|
/>]]></programlisting>
|
|
<calloutlist>
|
|
<callout arearefs="element1b">
|
|
<para>
|
|
<literal>column</literal> (optional): The name of the column holding the
|
|
collection element values.
|
|
</para>
|
|
</callout>
|
|
<callout arearefs="element2b">
|
|
<para>
|
|
<literal>formula</literal> (optional): An SQL formula used to evaluate the
|
|
element.
|
|
</para>
|
|
</callout>
|
|
<callout arearefs="element3b">
|
|
<para>
|
|
<literal>type</literal> (required): The type of the collection element.
|
|
</para>
|
|
</callout>
|
|
</calloutlist>
|
|
</programlistingco>
|
|
|
|
<para>
|
|
A <emphasis>many-to-many association</emphasis> is specified using the
|
|
<literal><many-to-many></literal> element.
|
|
</para>
|
|
|
|
<programlistingco>
|
|
<areaspec>
|
|
<area id="manytomany1" coords="2 60"/>
|
|
<area id="manytomany2" coords="3 60"/>
|
|
<area id="manytomany3" coords="4 60"/>
|
|
<area id="manytomany4" coords="5 60"/>
|
|
<area id="manytomany5" coords="6 60"/>
|
|
<area id="manytomany6" coords="7 60"/>
|
|
<area id="manytomany7" coords="8 60"/>
|
|
</areaspec>
|
|
<programlisting><![CDATA[<many-to-many
|
|
column="column_name"
|
|
formula="any SQL expression"
|
|
class="ClassName"
|
|
fetch="select|join"
|
|
unique="true|false"
|
|
not-found="ignore|exception"
|
|
entity-name="EntityName"
|
|
node="element-name"
|
|
embed-xml="true|false"
|
|
/>]]></programlisting>
|
|
<calloutlist>
|
|
<callout arearefs="manytomany1">
|
|
<para>
|
|
<literal>column</literal> (optional): The name of the element foreign key column.
|
|
</para>
|
|
</callout>
|
|
<callout arearefs="manytomany2">
|
|
<para>
|
|
<literal>formula</literal> (optional): An SQL formula used to evaluate the element
|
|
foreign key value.
|
|
</para>
|
|
</callout>
|
|
<callout arearefs="manytomany3">
|
|
<para>
|
|
<literal>class</literal> (required): The name of the associated class.
|
|
</para>
|
|
</callout>
|
|
<callout arearefs="manytomany4">
|
|
<para>
|
|
<literal>fetch</literal> (optional - defaults to <literal>join</literal>):
|
|
enables outer-join or sequential select fetching for this association. This
|
|
is a special case; for full eager fetching (in a single <literal>SELECT</literal>)
|
|
of an entity and its many-to-many relationships to other entities, you would
|
|
enable <literal>join</literal> fetching not only of the collection itself,
|
|
but also with this attribute on the <literal><many-to-many></literal>
|
|
nested element.
|
|
</para>
|
|
</callout>
|
|
<callout arearefs="manytomany5">
|
|
<para>
|
|
<literal>unique</literal> (optional): Enable the DDL generation of a unique
|
|
constraint for the foreign-key column. This makes the association multiplicity
|
|
effectively one to many.
|
|
</para>
|
|
</callout>
|
|
<callout arearefs="manytomany6">
|
|
<para>
|
|
<literal>not-found</literal> (optional - defaults to <literal>exception</literal>):
|
|
Specifies how foreign keys that reference missing rows will be handled:
|
|
<literal>ignore</literal> will treat a missing row as a null association.
|
|
</para>
|
|
</callout>
|
|
<callout arearefs="manytomany7">
|
|
<para>
|
|
<literal>entity-name</literal> (optional): The entity name of the associated class,
|
|
as an alternative to <literal>class</literal>.
|
|
</para>
|
|
</callout>
|
|
</calloutlist>
|
|
</programlistingco>
|
|
|
|
<para>
|
|
Some examples, first, a set of strings:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[<set name="names" table="person_names">
|
|
<key column="person_id"/>
|
|
<element column="person_name" type="string"/>
|
|
</set>]]></programlisting>
|
|
|
|
<para>
|
|
A bag containing integers (with an iteration order determined by the
|
|
<literal>order-by</literal> attribute):
|
|
</para>
|
|
|
|
<programlisting><![CDATA[<bag name="sizes"
|
|
table="item_sizes"
|
|
order-by="size asc">
|
|
<key column="item_id"/>
|
|
<element column="size" type="integer"/>
|
|
</bag>]]></programlisting>
|
|
|
|
<para>
|
|
An array of entities - in this case, a many to many association:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[<array name="addresses"
|
|
table="PersonAddress"
|
|
cascade="persist">
|
|
<key column="personId"/>
|
|
<list-index column="sortOrder"/>
|
|
<many-to-many column="addressId" class="Address"/>
|
|
</array>]]></programlisting>
|
|
|
|
<para>
|
|
A map from string indices to dates:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[<map name="holidays"
|
|
table="holidays"
|
|
schema="dbo"
|
|
order-by="hol_name asc">
|
|
<key column="id"/>
|
|
<map-key column="hol_name" type="string"/>
|
|
<element column="hol_date" type="date"/>
|
|
</map>]]></programlisting>
|
|
|
|
<para>
|
|
A list of components (discussed in the next chapter):
|
|
</para>
|
|
|
|
<programlisting><![CDATA[<list name="carComponents"
|
|
table="CarComponents">
|
|
<key column="carId"/>
|
|
<list-index column="sortOrder"/>
|
|
<composite-element class="CarComponent">
|
|
<property name="price"/>
|
|
<property name="type"/>
|
|
<property name="serialNumber" column="serialNum"/>
|
|
</composite-element>
|
|
</list>]]></programlisting>
|
|
|
|
</sect2>
|
|
|
|
<sect2 id="collections-onetomany">
|
|
<title>One-to-many associations</title>
|
|
|
|
<para>
|
|
A <emphasis>one to many association</emphasis> links the tables of two classes
|
|
via a foreign key, with no intervening collection table. This mapping loses
|
|
certain semantics of normal Java collections:
|
|
</para>
|
|
|
|
<itemizedlist spacing="compact">
|
|
<listitem>
|
|
<para>
|
|
An instance of the contained entity class may not belong to more than
|
|
one instance of the collection
|
|
</para>
|
|
</listitem>
|
|
<listitem>
|
|
<para>
|
|
An instance of the contained entity class may not appear at more than
|
|
one value of the collection index
|
|
</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
|
|
<para>
|
|
An association from <literal>Product</literal> to <literal>Part</literal> requires
|
|
existence of a foreign key column and possibly an index column to the <literal>Part</literal>
|
|
table. A <literal><one-to-many></literal> tag indicates that this is a one to many
|
|
association.
|
|
</para>
|
|
|
|
<programlistingco>
|
|
<areaspec>
|
|
<area id="onetomany1" coords="2 60"/>
|
|
<area id="onetomany2" coords="3 60"/>
|
|
<area id="onetomany3" coords="4 60"/>
|
|
</areaspec>
|
|
<programlisting><![CDATA[<one-to-many
|
|
class="ClassName"
|
|
not-found="ignore|exception"
|
|
entity-name="EntityName"
|
|
node="element-name"
|
|
embed-xml="true|false"
|
|
/>]]></programlisting>
|
|
<calloutlist>
|
|
<callout arearefs="onetomany1">
|
|
<para>
|
|
<literal>class</literal> (required): The name of the associated class.
|
|
</para>
|
|
</callout>
|
|
<callout arearefs="onetomany2">
|
|
<para>
|
|
<literal>not-found</literal> (optional - defaults to <literal>exception</literal>):
|
|
Specifies how cached identifiers that reference missing rows will be handled:
|
|
<literal>ignore</literal> will treat a missing row as a null association.
|
|
</para>
|
|
</callout>
|
|
<callout arearefs="onetomany3">
|
|
<para>
|
|
<literal>entity-name</literal> (optional): The entity name of the associated class,
|
|
as an alternative to <literal>class</literal>.
|
|
</para>
|
|
</callout>
|
|
</calloutlist>
|
|
</programlistingco>
|
|
|
|
<para>
|
|
Notice that the <literal><one-to-many></literal> element does not need to
|
|
declare any columns. Nor is it necessary to specify the <literal>table</literal>
|
|
name anywhere.
|
|
</para>
|
|
|
|
<para>
|
|
<emphasis>Very important note:</emphasis> If the foreign key column of a
|
|
<literal><one-to-many></literal> association is declared <literal>NOT NULL</literal>,
|
|
you must declare the <literal><key></literal> mapping
|
|
<literal>not-null="true"</literal> or <emphasis>use a bidirectional association</emphasis>
|
|
with the collection mapping marked <literal>inverse="true"</literal>. See the discussion
|
|
of bidirectional associations later in this chapter.
|
|
</para>
|
|
|
|
<para>
|
|
This example shows a map of <literal>Part</literal> entities by name (where
|
|
<literal>partName</literal> is a persistent property of <literal>Part</literal>).
|
|
Notice the use of a formula-based index.
|
|
</para>
|
|
|
|
<programlisting><![CDATA[<map name="parts"
|
|
cascade="all">
|
|
<key column="productId" not-null="true"/>
|
|
<map-key formula="partName"/>
|
|
<one-to-many class="Part"/>
|
|
</map>]]></programlisting>
|
|
</sect2>
|
|
|
|
</sect1>
|
|
|
|
<sect1 id="collections-advancedmappings">
|
|
<title>Advanced collection mappings</title>
|
|
|
|
<sect2 id="collections-sorted" revision="2">
|
|
<title>Sorted collections</title>
|
|
|
|
<para>
|
|
Hibernate supports collections implementing <literal>java.util.SortedMap</literal> and
|
|
<literal>java.util.SortedSet</literal>. You must specify a comparator in the mapping file:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[<set name="aliases"
|
|
table="person_aliases"
|
|
sort="natural">
|
|
<key column="person"/>
|
|
<element column="name" type="string"/>
|
|
</set>
|
|
|
|
<map name="holidays" sort="my.custom.HolidayComparator">
|
|
<key column="year_id"/>
|
|
<map-key column="hol_name" type="string"/>
|
|
<element column="hol_date" type="date"/>
|
|
</map>]]></programlisting>
|
|
|
|
<para>
|
|
Allowed values of the <literal>sort</literal> attribute are <literal>unsorted</literal>,
|
|
<literal>natural</literal> and the name of a class implementing
|
|
<literal>java.util.Comparator</literal>.
|
|
</para>
|
|
|
|
<para>
|
|
Sorted collections actually behave like <literal>java.util.TreeSet</literal> or
|
|
<literal>java.util.TreeMap</literal>.
|
|
</para>
|
|
|
|
<para>
|
|
If you want the database itself to order the collection elements use the
|
|
<literal>order-by</literal> attribute of <literal>set</literal>, <literal>bag</literal>
|
|
or <literal>map</literal> mappings. This solution is only available under
|
|
JDK 1.4 or higher (it is implemented using <literal>LinkedHashSet</literal> or
|
|
<literal>LinkedHashMap</literal>). This performs the ordering in the SQL query,
|
|
not in memory.
|
|
</para>
|
|
|
|
<programlisting><![CDATA[<set name="aliases" table="person_aliases" order-by="lower(name) asc">
|
|
<key column="person"/>
|
|
<element column="name" type="string"/>
|
|
</set>
|
|
|
|
<map name="holidays" order-by="hol_date, hol_name">
|
|
<key column="year_id"/>
|
|
<map-key column="hol_name" type="string"/>
|
|
<element column="hol_date type="date"/>
|
|
</map>]]></programlisting>
|
|
|
|
<para>
|
|
Note that the value of the <literal>order-by</literal> attribute is an SQL ordering, not
|
|
a HQL ordering!
|
|
</para>
|
|
|
|
<para>
|
|
Associations may even be sorted by some arbitrary criteria at runtime using a collection
|
|
<literal>filter()</literal>.
|
|
</para>
|
|
|
|
<programlisting><![CDATA[sortedUsers = s.createFilter( group.getUsers(), "order by this.name" ).list();]]></programlisting>
|
|
|
|
</sect2>
|
|
|
|
<sect2 id="collections-bidirectional" revision="1">
|
|
<title>Bidirectional associations</title>
|
|
|
|
<para>
|
|
A <emphasis>bidirectional association</emphasis> allows navigation from both
|
|
"ends" of the association. Two kinds of bidirectional association are
|
|
supported:
|
|
|
|
<variablelist>
|
|
<varlistentry>
|
|
<term>one-to-many</term>
|
|
<listitem>
|
|
<para>
|
|
set or bag valued at one end, single-valued at the other
|
|
</para>
|
|
</listitem>
|
|
</varlistentry>
|
|
<varlistentry>
|
|
<term>many-to-many</term>
|
|
<listitem>
|
|
<para>
|
|
set or bag valued at both ends
|
|
</para>
|
|
</listitem>
|
|
</varlistentry>
|
|
</variablelist>
|
|
|
|
</para>
|
|
|
|
<para>
|
|
You may specify a bidirectional many-to-many association simply by mapping two
|
|
many-to-many associations to the same database table and declaring one end as
|
|
<emphasis>inverse</emphasis> (which one is your choice, but it can not be an
|
|
indexed collection).
|
|
</para>
|
|
|
|
<para>
|
|
Here's an example of a bidirectional many-to-many association; each category can
|
|
have many items and each item can be in many categories:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[<class name="Category">
|
|
<id name="id" column="CATEGORY_ID"/>
|
|
...
|
|
<bag name="items" table="CATEGORY_ITEM">
|
|
<key column="CATEGORY_ID"/>
|
|
<many-to-many class="Item" column="ITEM_ID"/>
|
|
</bag>
|
|
</class>
|
|
|
|
<class name="Item">
|
|
<id name="id" column="CATEGORY_ID"/>
|
|
...
|
|
|
|
<!-- inverse end -->
|
|
<bag name="categories" table="CATEGORY_ITEM" inverse="true">
|
|
<key column="ITEM_ID"/>
|
|
<many-to-many class="Category" column="CATEGORY_ID"/>
|
|
</bag>
|
|
</class>]]></programlisting>
|
|
|
|
<para>
|
|
Changes made only to the inverse end of the association are <emphasis>not</emphasis>
|
|
persisted. This means that Hibernate has two representations in memory for every
|
|
bidirectional association, one link from A to B and another link from B to A. This
|
|
is easier to understand if you think about the Java object model and how we create
|
|
a many-to-many relationship in Java:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[
|
|
category.getItems().add(item); // The category now "knows" about the relationship
|
|
item.getCategories().add(category); // The item now "knows" about the relationship
|
|
|
|
session.persist(item); // The relationship won't be saved!
|
|
session.persist(category); // The relationship will be saved]]></programlisting>
|
|
|
|
<para>
|
|
The non-inverse side is used to save the in-memory representation to the database.
|
|
</para>
|
|
|
|
<para>
|
|
You may define a bidirectional one-to-many association by mapping a one-to-many association
|
|
to the same table column(s) as a many-to-one association and declaring the many-valued
|
|
end <literal>inverse="true"</literal>.
|
|
</para>
|
|
|
|
<programlisting><![CDATA[<class name="Parent">
|
|
<id name="id" column="parent_id"/>
|
|
....
|
|
<set name="children" inverse="true">
|
|
<key column="parent_id"/>
|
|
<one-to-many class="Child"/>
|
|
</set>
|
|
</class>
|
|
|
|
<class name="eg.Child">
|
|
<id name="id" column="id"/>
|
|
....
|
|
<many-to-one name="parent"
|
|
class="Parent"
|
|
column="parent_id"
|
|
not-null="true"/>
|
|
</class>]]></programlisting>
|
|
|
|
<para>
|
|
Mapping one end of an association with <literal>inverse="true"</literal> doesn't
|
|
affect the operation of cascades, these are orthogonal concepts!
|
|
</para>
|
|
|
|
</sect2>
|
|
|
|
<sect2 id="collections-ternary">
|
|
<title>Ternary associations</title>
|
|
|
|
<para>
|
|
There are three possible approaches to mapping a ternary association. One is to use a
|
|
<literal>Map</literal> with an association as its index:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[<map name="contracts">
|
|
<key column="employer_id" not-null="true"/>
|
|
<map-key-many-to-many column="employee_id" class="Employee"/>
|
|
<one-to-many class="Contract"/>
|
|
</map>]]></programlisting>
|
|
|
|
<programlisting><![CDATA[<map name="connections">
|
|
<key column="incoming_node_id"/>
|
|
<map-key-many-to-many column="outgoing_node_id" class="Node"/>
|
|
<many-to-many column="connection_id" class="Connection"/>
|
|
</map>]]></programlisting>
|
|
|
|
<para>
|
|
A second approach is to simply remodel the association as an entity class. This
|
|
is the approach we use most commonly.
|
|
</para>
|
|
|
|
<para>
|
|
A final alternative is to use composite elements, which we will discuss later.
|
|
</para>
|
|
|
|
</sect2>
|
|
|
|
<sect2 id="collections-idbag" revision="1">
|
|
<title><literal>Using an <idbag></literal></title>
|
|
|
|
<para>
|
|
If you've fully embraced our view that composite keys are a bad thing and that
|
|
entities should have synthetic identifiers (surrogate keys), then you might
|
|
find it a bit odd that the many to many associations and collections of values
|
|
that we've shown so far all map to tables with composite keys! Now, this point
|
|
is quite arguable; a pure association table doesn't seem to benefit much from
|
|
a surrogate key (though a collection of composite values <emphasis>might</emphasis>).
|
|
Nevertheless, Hibernate provides a feature that allows you to map many to many
|
|
associations and collections of values to a table with a surrogate key.
|
|
</para>
|
|
|
|
<para>
|
|
The <literal><idbag></literal> element lets you map a <literal>List</literal>
|
|
(or <literal>Collection</literal>) with bag semantics.
|
|
</para>
|
|
|
|
<programlisting><![CDATA[<idbag name="lovers" table="LOVERS">
|
|
<collection-id column="ID" type="long">
|
|
<generator class="sequence"/>
|
|
</collection-id>
|
|
<key column="PERSON1"/>
|
|
<many-to-many column="PERSON2" class="eg.Person" outer-join="true"/>
|
|
</idbag>]]></programlisting>
|
|
|
|
<para>
|
|
As you can see, an <literal><idbag></literal> has a synthetic id generator,
|
|
just like an entity class! A different surrogate key is assigned to each collection
|
|
row. Hibernate does not provide any mechanism to discover the surrogate key value
|
|
of a particular row, however.
|
|
</para>
|
|
|
|
<para>
|
|
Note that the update performance of an <literal><idbag></literal> is
|
|
<emphasis>much</emphasis> better than a regular <literal><bag></literal>!
|
|
Hibernate can locate individual rows efficiently and update or delete them
|
|
individually, just like a list, map or set.
|
|
</para>
|
|
|
|
<para>
|
|
In the current implementation, the <literal>native</literal> identifier generation
|
|
strategy is not supported for <literal><idbag></literal> collection identifiers.
|
|
</para>
|
|
|
|
</sect2>
|
|
|
|
</sect1>
|
|
|
|
<!--undocumenting this stuff -->
|
|
|
|
<!--sect1 id="collections-heterogeneous">
|
|
<title>Heterogeneous Associations</title>
|
|
|
|
<para>
|
|
The <literal><many-to-any></literal> and <literal><index-many-to-any></literal>
|
|
elements provide for true heterogeneous associations. These mapping elements work in the
|
|
same way as the <literal><any></literal> element - and should also be used
|
|
rarely, if ever.
|
|
</para>
|
|
|
|
</sect1-->
|
|
|
|
<sect1 id="collections-example" revision="1">
|
|
<title>Collection examples</title>
|
|
|
|
<para>
|
|
The previous sections are pretty confusing. So lets look at an example. This
|
|
class:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[package eg;
|
|
import java.util.Set;
|
|
|
|
public class Parent {
|
|
private long id;
|
|
private Set children;
|
|
|
|
public long getId() { return id; }
|
|
private void setId(long id) { this.id=id; }
|
|
|
|
private Set getChildren() { return children; }
|
|
private void setChildren(Set children) { this.children=children; }
|
|
|
|
....
|
|
....
|
|
}]]></programlisting>
|
|
|
|
<para>
|
|
has a collection of <literal>Child</literal> instances. If each
|
|
child has at most one parent, the most natural mapping is a
|
|
one-to-many association:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[<hibernate-mapping>
|
|
|
|
<class name="Parent">
|
|
<id name="id">
|
|
<generator class="sequence"/>
|
|
</id>
|
|
<set name="children">
|
|
<key column="parent_id"/>
|
|
<one-to-many class="Child"/>
|
|
</set>
|
|
</class>
|
|
|
|
<class name="Child">
|
|
<id name="id">
|
|
<generator class="sequence"/>
|
|
</id>
|
|
<property name="name"/>
|
|
</class>
|
|
|
|
</hibernate-mapping>]]></programlisting>
|
|
|
|
<para>
|
|
This maps to the following table definitions:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[create table parent ( id bigint not null primary key )
|
|
create table child ( id bigint not null primary key, name varchar(255), parent_id bigint )
|
|
alter table child add constraint childfk0 (parent_id) references parent]]></programlisting>
|
|
|
|
<para>
|
|
If the parent is <emphasis>required</emphasis>, use a bidirectional one-to-many
|
|
association:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[<hibernate-mapping>
|
|
|
|
<class name="Parent">
|
|
<id name="id">
|
|
<generator class="sequence"/>
|
|
</id>
|
|
<set name="children" inverse="true">
|
|
<key column="parent_id"/>
|
|
<one-to-many class="Child"/>
|
|
</set>
|
|
</class>
|
|
|
|
<class name="Child">
|
|
<id name="id">
|
|
<generator class="sequence"/>
|
|
</id>
|
|
<property name="name"/>
|
|
<many-to-one name="parent" class="Parent" column="parent_id" not-null="true"/>
|
|
</class>
|
|
|
|
</hibernate-mapping>]]></programlisting>
|
|
|
|
<para>
|
|
Notice the <literal>NOT NULL</literal> constraint:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[create table parent ( id bigint not null primary key )
|
|
create table child ( id bigint not null
|
|
primary key,
|
|
name varchar(255),
|
|
parent_id bigint not null )
|
|
alter table child add constraint childfk0 (parent_id) references parent]]></programlisting>
|
|
|
|
<para>
|
|
Alternatively, if you absolutely insist that this association should be unidirectional,
|
|
you can declare the <literal>NOT NULL</literal> constraint on the <literal><key></literal>
|
|
mapping:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[<hibernate-mapping>
|
|
|
|
<class name="Parent">
|
|
<id name="id">
|
|
<generator class="sequence"/>
|
|
</id>
|
|
<set name="children">
|
|
<key column="parent_id" not-null="true"/>
|
|
<one-to-many class="Child"/>
|
|
</set>
|
|
</class>
|
|
|
|
<class name="Child">
|
|
<id name="id">
|
|
<generator class="sequence"/>
|
|
</id>
|
|
<property name="name"/>
|
|
</class>
|
|
|
|
</hibernate-mapping>]]></programlisting>
|
|
|
|
<para>
|
|
On the other hand, if a child might have multiple parents, a many-to-many
|
|
association is appropriate:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[<hibernate-mapping>
|
|
|
|
<class name="Parent">
|
|
<id name="id">
|
|
<generator class="sequence"/>
|
|
</id>
|
|
<set name="children" table="childset">
|
|
<key column="parent_id"/>
|
|
<many-to-many class="Child" column="child_id"/>
|
|
</set>
|
|
</class>
|
|
|
|
<class name="Child">
|
|
<id name="id">
|
|
<generator class="sequence"/>
|
|
</id>
|
|
<property name="name"/>
|
|
</class>
|
|
|
|
</hibernate-mapping>]]></programlisting>
|
|
|
|
<para>
|
|
Table definitions:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[create table parent ( id bigint not null primary key )
|
|
create table child ( id bigint not null primary key, name varchar(255) )
|
|
create table childset ( parent_id bigint not null,
|
|
child_id bigint not null,
|
|
primary key ( parent_id, child_id ) )
|
|
alter table childset add constraint childsetfk0 (parent_id) references parent
|
|
alter table childset add constraint childsetfk1 (child_id) references child]]></programlisting>
|
|
|
|
<para>
|
|
For more examples and a complete walk-through a parent/child relationship mapping,
|
|
see <xref linkend="example-parentchild"/>.
|
|
</para>
|
|
|
|
<para>
|
|
Even more exotic association mappings are possible, we will catalog all possibilities
|
|
in the next chapter.
|
|
</para>
|
|
|
|
</sect1>
|
|
|
|
</chapter>
|