hibernate-orm/reference/en/modules/collection_mapping.xml

1123 lines
45 KiB
XML
Raw Normal View History

<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>&lt;set&gt;</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>&lt;set&gt;</literal>, there is also
<literal>&lt;list&gt;</literal>, <literal>&lt;map&gt;</literal>,
<literal>&lt;bag&gt;</literal>, <literal>&lt;array&gt;</literal> and
<literal>&lt;primitive-array&gt;</literal> mapping elements. The
<literal>&lt;map&gt;</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"
>
<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>)
enable lazy initialization (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. Only one collection may be fetched by outer join per SQL
<literal>SELECT</literal>.
</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>&lt;key&gt;</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>&lt;key&gt;</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>&lt;element&gt;</literal> or
<literal>&lt;composite-element&gt;</literal>, or in the case of entity references,
with <literal>&lt;one-to-many&gt;</literal> or <literal>&lt;many-to-many&gt;</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>&lt;map-key&gt;</literal>, it may be an entity reference mapped with
<literal>&lt;map-key-many-to-many&gt;</literal>, or it may be a composite type,
mapped with <literal>&lt;composite-map-key&gt;</literal>. The index of an array or
list is always of type <literal>integer</literal> and is mapped using the
<literal>&lt;list-index&gt;</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"
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>&lt;bag&gt;</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>&lt;element&gt;</literal> tag.
</para>
<programlistingco>
<areaspec>
<area id="element1b" coords="2 45"/>
<area id="element2b" coords="3 45"/>
<area id="element3b" coords="4 45"/>
</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"
/>]]></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>&lt;many-to-many&gt;</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"/>
</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"
/>]]></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>&lt;many-to-many&gt;</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>
</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="create">
<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>&lt;one-to-many&gt;</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"/>
</areaspec>
<programlisting><![CDATA[<one-to-many
class="ClassName"
not-found="ignore|exception"
entity-name="EntityName"/>]]></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>
</calloutlist>
</programlistingco>
<para>
Notice that the <literal>&lt;one-to-many&gt;</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>&lt;one-to-many&gt;</literal> association is declared <literal>NOT NULL</literal>,
you must declare the <literal>&lt;key&gt;</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 &lt;idbag&gt;</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>&lt;idbag&gt;</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>&lt;idbag&gt;</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>&lt;idbag&gt;</literal> is
<emphasis>much</emphasis> better than a regular <literal>&lt;bag&gt;</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>&lt;idbag&gt;</literal> collection identifiers.
</para>
</sect2>
</sect1>
<!--undocumenting this stuff -->
<!--sect1 id="collections-heterogeneous">
<title>Heterogeneous Associations</title>
<para>
The <literal>&lt;many-to-any&gt;</literal> and <literal>&lt;index-many-to-any&gt;</literal>
elements provide for true heterogeneous associations. These mapping elements work in the
same way as the <literal>&lt;any&gt;</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>&lt;key&gt;</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>