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

1076 lines
45 KiB
XML
Raw Normal View History

<chapter id="collections">
<title>Collection Mapping</title>
<sect1 id="collections-persistent">
<title>Persistent Collections</title>
<para>
This section does not contain much example Java code. We assume you already know
how to use Java's collections framework. If so, there's not really anything more
to know - with a single caveat, you may use Java collections the same way you
always have.
</para>
<para>
Hibernate can persist instances of
<literal>java.util.Map</literal>,
<literal>java.util.Set</literal>,
<literal>java.util.SortedMap</literal>,
<literal>java.util.SortedSet</literal>,
<literal>java.util.List</literal>,
and any array of persistent entities or values. Properties of type
<literal>java.util.Collection</literal> or
<literal>java.util.List</literal>
may also be persisted with "bag" semantics.
</para>
<para>
Now the caveat: persistent collections do not retain any extra semantics added by the class
implementing the collection interface (eg. iteration order of a <literal>LinkedHashSet</literal>).
The persistent collections actually behave like
<literal>HashMap</literal>,
<literal>HashSet</literal>,
<literal>TreeMap</literal>,
<literal>TreeSet</literal> and
<literal>ArrayList</literal>
respectively. Furthermore, the Java type of a property holding a collection must be
the interface type (ie. <literal>Map</literal>, <literal>Set</literal> or
<literal>List</literal>; never <literal>HashMap</literal>, <literal>TreeSet</literal> or
<literal>ArrayList</literal>). This restriction exists because, when you're not looking,
Hibernate sneakily replaces your instances of <literal>Map</literal>, <literal>Set</literal>
and <literal>List</literal> with instances of its own persistent implementations of
<literal>Map</literal>, <literal>Set</literal> or <literal>List</literal>. (So also be careful
when using <literal>==</literal> on your collections.)
</para>
<programlisting><![CDATA[Cat cat = new DomesticCat();
Cat kitten = new DomesticCat();
....
Set kittens = new HashSet();
kittens.add(kitten);
cat.setKittens(kittens);
session.save(cat);
kittens = cat.getKittens(); //Okay, kittens collection is a Set
(HashSet) cat.getKittens(); //Error!]]></programlisting>
<para>
Collections obey the usual rules for value types: no shared
references, created and deleted along with containing entity. Due to the underlying
relational model, they do not support null value semantics; Hibernate does not
distinguish between a null collection reference and an empty collection.
</para>
<para>
Collections 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. You shouldn't have to worry much about any of this. Just use
Hibernate's collections the same way you use ordinary Java collections, but
make sure you understand the semantics of bidirectional associations (discussed
later) before using them.
</para>
<para>
Collection instances are distinguished in the database by a foreign key to
the owning entity. This foreign key is referred to as the
<emphasis>collection key </emphasis>. The collection key is mapped by
the <literal>&lt;key&gt;</literal> element.
</para>
<para>
Collections may contain almost any other Hibernate type, including all basic types,
custom types, entity types and components. This is an important definition: An object
in a collection can either be handled with "pass by value" semantics (it therefore
fully depends on the collection owner) or it can be a reference to another entity
with an own lifecycle. Collections may not contain other collections. The contained type
is referred to as the <emphasis>collection element type</emphasis>. Collection elements
are mapped by <literal>&lt;element&gt;</literal>, <literal>&lt;composite-element&gt;</literal>,
<literal>&lt;one-to-many&gt;</literal>, <literal>&lt;many-to-many&gt;</literal> or
<literal>&lt;many-to-any&gt;</literal>. The first two map elements with value semantics,
the other three are used to map entity associations.
</para>
<para>
All collection types except <literal>Set</literal> and bag have an <emphasis>index
</emphasis> column - a column that maps to an array or <literal>List</literal> index or
<literal>Map</literal> key. The index of a <literal>Map</literal> may be of any
basic type, an entity type or even a composite type (it may not be a collection). The
index of an array or list is always of type <literal>integer</literal>. Indexes are
mapped using <literal>&lt;index&gt;</literal>, <literal>&lt;index-many-to-many&gt;</literal>,
<literal>&lt;composite-index&gt;</literal> or <literal>&lt;index-many-to-any&gt;</literal>.
</para>
<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>
</sect1>
<sect1 id="collections-mapping">
<title>Mapping a Collection</title>
<para>
Collections are declared by the
<literal>&lt;set&gt;</literal>,
<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> elements.
<literal>&lt;map&gt;</literal> 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"/>
</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"
outer-join="true|false|auto"
batch-size="N"
access="field|property|ClassName"
>
<key .... />
<index .... />
<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>false</literal>)
enable lazy initialization (not used 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>outer-join</literal> (optional) specify that the collection should be fetched
by outer join, whenever possible. 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>
</calloutlist>
</programlistingco>
<para>
The mapping of a <literal>List</literal> or array requires a seperate table column holding the array
or list index (the <literal>i</literal> in <literal>foo[i]</literal>). If your relational model doesn't
have an index column, e.g. if you're working with legacy data, use an unordered <literal>Set</literal>
instead. This seems to put people off who assume that <literal>List</literal> should just be a more
convenient way of accessing an unordered collection. Hibernate collections strictly obey the actual
semantics attached to the <literal>Set</literal>, <literal>List</literal> and <literal>Map</literal>
interfaces. <literal>List</literal> elements don't just spontaneously rearrange themselves!
</para>
<para>
On the other hand, people who planned to use the <literal>List</literal> to emulate
<emphasis>bag</emphasis> semantics have a legitimate grievance here.
A bag is an unordered, unindexed collection which may contain the same element multiple times.
The Java collections framework lacks a <literal>Bag</literal> interface, hence you have to emulate
it with a <literal>List</literal>. Hibernate lets you map properties of type <literal>List</literal>
or <literal>Collection</literal> with the <literal>&lt;bag&gt;</literal> element. Note that bag
semantics are not really part of the <literal>Collection</literal> contract and they actually
conflict with the semantics of the <literal>List</literal> contract (however, you can sort
the bag arbitrarily, discussed later in this chapter).
</para>
<para>
Note: Large Hibernate bags mapped with <literal>inverse="false"</literal> are inefficient and
should be avoided; Hibernate can't create, delete or update rows individually, because there is
no key that may be used to identify an individual row.
</para>
</sect1>
<sect1 id="collections-ofvalues">
<title>Collections of Values and Many-To-Many Associations</title>
<para>
A collection table is required for any collection of values and any collection of
references to other entities mapped as a many-to-many association (the natural semantics
for a Java collection). The table requires (foreign) key column(s), element column(s) and
possibly index column(s).
</para>
<para>
The foreign key from the collection table to the table of the owning class is
declared using a <literal>&lt;key&gt;</literal> element.
</para>
<programlistingco>
<areaspec>
<area id="key1" coords="1 45"/>
</areaspec>
<programlisting><![CDATA[<key column="column_name"/>]]></programlisting>
<calloutlist>
<callout arearefs="element1">
<para>
<literal>column</literal> (required): The name of the foreign key column.
</para>
</callout>
</calloutlist>
</programlistingco>
<para>
For indexed collections like maps and lists, we require an <literal>&lt;index&gt;</literal>
element. For lists, this column contains sequential integers numbered from zero. Make sure
that your index really starts from zero if you have to deal with legacy data. For maps,
the column may contain any values of any Hibernate type.
</para>
<programlistingco>
<areaspec>
<area id="index1" coords="2 45"/>
<area id="index2" coords="3 45"/>
</areaspec>
<programlisting><![CDATA[<index
column="column_name"
type="typename"
/>]]></programlisting>
<calloutlist>
<callout arearefs="index1">
<para>
<literal>column</literal> (required): The name of the column holding the
collection index values.
</para>
</callout>
<callout arearefs="index2">
<para>
<literal>type</literal> (optional, defaults to <literal>integer</literal>):
The type of the collection index.
</para>
</callout>
</calloutlist>
</programlistingco>
<para>
Alternatively, a map may be indexed by objects of entity type. We use the
<literal>&lt;index-many-to-many&gt;</literal> element.
</para>
<programlistingco>
<areaspec>
<area id="indexmanytomany1" coords="2 45"/>
<area id="indexmanytomany2" coords="3 45"/>
</areaspec>
<programlisting><![CDATA[<index-many-to-many
column="column_name"
class="ClassName"
/>]]></programlisting>
<calloutlist>
<callout arearefs="indexmanytomany1">
<para>
<literal>column</literal> (required): The name of the foreign key
column for the collection index values.
</para>
</callout>
<callout arearefs="indexmanytomany2">
<para>
<literal>class</literal> (required): The entity class used as the
collection index.
</para>
</callout>
</calloutlist>
</programlistingco>
<para>
For a collection of values, we use the <literal>&lt;element&gt;</literal> tag.
</para>
<programlistingco>
<areaspec>
<area id="element1" coords="2 45"/>
<area id="element2" coords="3 45"/>
</areaspec>
<programlisting><![CDATA[<element
column="column_name"
type="typename"
/>]]></programlisting>
<calloutlist>
<callout arearefs="element1">
<para>
<literal>column</literal> (required): The name of the column holding the
collection element values.
</para>
</callout>
<callout arearefs="element2">
<para>
<literal>type</literal> (required): The type of the collection element.
</para>
</callout>
</calloutlist>
</programlistingco>
<para>
A collection of entities with its own table corresponds to the relational notion
of <emphasis>many-to-many association</emphasis>. A many to many association is the
most natural mapping of a Java collection but is not usually the best relational model.
</para>
<programlistingco>
<areaspec>
<area id="manytomany1" coords="2 60"/>
<area id="manytomany2" coords="3 60"/>
<area id="manytomany3" coords="4 60"/>
</areaspec>
<programlisting><![CDATA[<many-to-many
column="column_name"
class="ClassName"
outer-join="true|false|auto"
/>]]></programlisting>
<calloutlist>
<callout arearefs="manytomany1">
<para>
<literal>column</literal> (required): The name of the element foreign key column.
</para>
</callout>
<callout arearefs="manytomany2">
<para>
<literal>class</literal> (required): The name of the associated class.
</para>
</callout>
<callout arearefs="manytomany3">
<para>
<literal>outer-join</literal> (optional - defaults to <literal>auto</literal>):
enables outer-join fetching for this association when
<literal>hibernate.use_outer_join</literal> is set.
</para>
</callout>
</calloutlist>
</programlistingco>
<para>
Some examples, first, a set of strings:
</para>
<programlisting><![CDATA[<set name="names" table="NAMES">
<key column="GROUPID"/>
<element column="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="SIZES" order-by="SIZE ASC">
<key column="OWNER"/>
<element column="SIZE" type="integer"/>
</bag>]]></programlisting>
<para>
An array of entities - in this case, a many to many association (note that
the entities are lifecycle objects, <literal>cascade="all"</literal>):
</para>
<programlisting><![CDATA[<array name="foos" table="BAR_FOOS" cascade="all">
<key column="BAR_ID"/>
<index column="I"/>
<many-to-many column="FOO_ID" class="org.hibernate.Foo"/>
</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"/>
<index 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="car_components">
<key column="car_id"/>
<index column="posn"/>
<composite-element class="org.hibernate.car.CarComponent">
<property name="price" type="float"/>
<property name="type" type="org.hibernate.car.ComponentType"/>
<property name="serialNumber" column="serial_no" type="string"/>
</composite-element>
</list>]]></programlisting>
</sect1>
<sect1 id="collections-onetomany">
<title>One-To-Many Associations</title>
<para>
A <emphasis>one to many association</emphasis> links the tables of two classes
<emphasis>directly</emphasis>, with no intervening collection table.
(This implements a <emphasis>one-to-many</emphasis> relational model.) This
relational model loses some of the semantics of Java collections:
</para>
<itemizedlist spacing="compact">
<listitem>
<para>
No null values may be contained in a map, set or list
</para>
</listitem>
<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>Foo</literal> to <literal>Bar</literal> requires the
addition of a key column and possibly an index column to the table of the contained
entity class, <literal>Bar</literal>. These columns are mapped using the
<literal>&lt;key&gt;</literal> and <literal>&lt;index&gt;</literal> elements
described above.
</para>
<para>
The <literal>&lt;one-to-many&gt;</literal> tag indicates a one to many association.
</para>
<programlistingco>
<areaspec>
<area id="onetomany1" coords="1 60"/>
</areaspec>
<programlisting><![CDATA[<one-to-many class="ClassName"/>]]></programlisting>
<calloutlist>
<callout arearefs="onetomany1">
<para>
<literal>class</literal> (required): The name of the associated class.
</para>
</callout>
</calloutlist>
</programlistingco>
<para>
Example:
</para>
<programlisting><![CDATA[<set name="bars">
<key column="foo_id"/>
<one-to-many class="org.hibernate.Bar"/>
</set>]]></programlisting>
<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 <literal>&lt;key&gt;</literal>
column of a <literal>&lt;one-to-many&gt;</literal> association is declared
<literal>NOT NULL</literal>, Hibernate may cause constraint violations
when it creates or updates the association. To prevent this problem,
<emphasis>you must use a bidirectional association</emphasis> with the many valued
end (the set or bag) marked as <literal>inverse="true"</literal>.
See the discussion of bidirectional associations later in this chapter.
</para>
</sect1>
<sect1 id="collections-lazy">
<title>Lazy Initialization</title>
<para>
Collections (other than arrays) may be lazily initialized, meaning they load
their state from the database only when the application needs to access it.
Initialization happens transparently to the user so the application would not
normally need to worry about this (in fact, transparent lazy initialization is
the main reason why Hibernate needs its own collection implementations).
However, if the application tries something like this:
</para>
<programlisting><![CDATA[s = sessions.openSession();
User u = (User) s.find("from User u where u.name=?", userName, Hibernate.STRING).get(0);
Map permissions = u.getPermissions();
s.connection().commit();
s.close();
Integer accessLevel = (Integer) permissions.get("accounts"); // Error!]]></programlisting>
<para>
It could be in for a nasty surprise. Since the permissions collection was not
initialized when the <literal>Session</literal> was committed,
the collection will never be able to load its state. The fix is to move the
line that reads from the collection to just before the commit. (There are
other more advanced ways to solve this problem, however.)
</para>
<para>
Alternatively, use a non-lazy collection. Since lazy initialization can lead to
bugs like that above, non-laziness is the default. However, it is intended that
lazy initialization be used for almost all collections, especially for
collections of entities (for reasons of efficiency).
</para>
<para>
Exceptions that occur while lazily initializing a collection are wrapped in a
<literal>LazyInitializationException</literal>.
</para>
<para>
Declare a lazy collection using the optional <literal>lazy</literal> attribute:
</para>
<programlisting><![CDATA[<set name="names" table="NAMES" lazy="true">
<key column="group_id"/>
<element column="NAME" type="string"/>
</set>]]></programlisting>
<para>
In some application architectures, particularly where the code that accesses data
using Hibernate, and the code that uses it are in different application layers, it
can be a problem to ensure that the <literal>Session</literal> is open when a
collection is initialized. They are two basic ways to deal with this issue:
</para>
<itemizedlist>
<listitem>
<para>
In a web-based application, a servlet filter can be used to close the
<literal>Session</literal> only at the very end of a user request, once
the rendering of the view is complete. Of course, this places heavy
demands upon the correctness of the exception handling of your application
infrastructure. It is vitally important that the <literal>Session</literal>
is closed and the transaction ended before returning to the user, even
when an exception occurs during rendering of the view. The servlet filter
has to be able to access the <literal>Session</literal> for this approach.
We recommend that a <literal>ThreadLocal</literal> variable be used to
hold the current <literal>Session</literal> (see chapter 1,
<xref linkend="quickstart-playingwithcats"/>, for an example implementation).
</para>
</listitem>
<listitem>
<para>
In an application with a seperate business tier, the business logic must
"prepare" all collections that will be needed by the web tier before
returning. This means that the business tier should load all the data and
return all the data already initialized to the presentation/web tier that
is required for a particular use case. Usually, the application calls
<literal>Hibernate.initialize()</literal> for each collection that will
be needed in the web tier (this call must occur before the session is closed)
or retrieves the collection eagerly using a Hibernate query with a
<literal>FETCH</literal> clause.
</para>
</listitem>
<listitem>
<para>
You may also attach a previously loaded object to a new <literal>Session</literal>
with <literal>update()</literal> or <literal>lock()</literal> before
accessing unitialized collections (or other proxies). Hibernate can not
do this automatically, as it would introduce ad hoc transaction semantics!
</para>
</listitem>
</itemizedlist>
<para>
You can use the <literal>filter()</literal> method of the Hibernate Session API to
get the size of a collection without initializing it:
</para>
<programlisting><![CDATA[( (Integer) s.filter( collection, "select count(*)" ).get(0) ).intValue()]]></programlisting>
<para>
<literal>filter()</literal> or <literal>createFilter()</literal> are also used to
efficiently retrieve subsets of a collection without needing to initialize the whole
collection.
</para>
</sect1>
<sect1 id="collections-sorted" revision="1">
<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" lazy="true">
<key column="year_id"/>
<index 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="name asc">
<key column="person"/>
<element column="name" type="string"/>
</set>
<map name="holidays" order-by="hol_date, hol_name" lazy="true">
<key column="year_id"/>
<index 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
<literal>filter()</literal>.
</para>
<programlisting><![CDATA[sortedUsers = s.filter( group.getUsers(), "order by this.name" );]]></programlisting>
</sect1>
<sect1 id="collections-idbag">
<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 (slightly experimental) 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" lazy="true">
<collection-id column="ID" type="long">
<generator class="hilo"/>
</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>identity</literal> identifier generation
strategy is not supported for <literal>&lt;idbag&gt;</literal> collection identifiers.
</para>
</sect1>
<sect1 id="collections-bidirectional">
<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>
Please note that Hibernate does not support bidirectional one-to-many associations
with an indexed collection (list, map or array) as the "many" end, you have to
use a set or bag mapping.
</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). Here's an example of
a bidirectional many-to-many association from a class back to <emphasis>itself</emphasis>
(each category can have many items and each item can be in many categories):
</para>
<programlisting><![CDATA[<class name="org.hibernate.auction.Category">
<id name="id" column="ID"/>
...
<bag name="items" table="CATEGORY_ITEM" lazy="true">
<key column="CATEGORY_ID"/>
<many-to-many class="org.hibernate.auction.Item" column="ITEM_ID"/>
</bag>
</class>
<class name="org.hibernate.auction.Item">
<id name="id" column="ID"/>
...
<!-- inverse end -->
<bag name="categories" table="CATEGORY_ITEM" inverse="true" lazy="true">
<key column="ITEM_ID"/>
<many-to-many class="org.hibernate.auction.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.update(item); // No effect, nothing will be saved!
session.update(category); // The relationship will be saved]]></programlisting>
<para>
The non-inverse side is used to save the in-memory representation to the database.
We would get an unneccessary INSERT/UPDATE and probably even a foreign key violation
if both would trigger changes! The same is of course also true for bidirectional
one-to-many associations.
</para>
<para>
You may map 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="eg.Parent">
<id name="id" column="id"/>
....
<set name="children" inverse="true" lazy="true">
<key column="parent_id"/>
<one-to-many class="eg.Child"/>
</set>
</class>
<class name="eg.Child">
<id name="id" column="id"/>
....
<many-to-one name="parent" class="eg.Parent" column="parent_id"/>
</class>]]></programlisting>
<para>
Mapping one end of an association with <literal>inverse="true"</literal> doesn't
affect the operation of cascades, both are different concepts!
</para>
</sect1>
<sect1 id="collections-ternary">
<title>Ternary Associations</title>
<para>
There are two possible approaches to mapping a ternary association. One approach is to use
composite elements (discussed below). Another is to use a <literal>Map</literal> with an
association as its index:
</para>
<programlisting><![CDATA[<map name="contracts" lazy="true">
<key column="employer_id"/>
<index-many-to-many column="employee_id" class="Employee"/>
<one-to-many column="contract_id" class="Contract"/>
</map>]]></programlisting>
<programlisting><![CDATA[<map name="connections" lazy="true">
<key column="node1_id"/>
<index-many-to-many column="node2_id" class="Node"/>
<many-to-many column="connection_id" class="Connection"/>
</map>]]></programlisting>
</sect1>
<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">
<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>eg.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="eg.Parent">
<id name="id">
<generator class="sequence"/>
</id>
<set name="children" lazy="true">
<key column="parent_id"/>
<one-to-many class="eg.Child"/>
</set>
</class>
<class name="eg.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="eg.Parent">
<id name="id">
<generator class="sequence"/>
</id>
<set name="children" inverse="true" lazy="true">
<key column="parent_id"/>
<one-to-many class="eg.Child"/>
</set>
</class>
<class name="eg.Child">
<id name="id">
<generator class="sequence"/>
</id>
<property name="name"/>
<many-to-one name="parent" class="eg.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>
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="eg.Parent">
<id name="id">
<generator class="sequence"/>
</id>
<set name="children" lazy="true" table="childset">
<key column="parent_id"/>
<many-to-many class="eg.Child" column="child_id"/>
</set>
</class>
<class name="eg.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>
<!-- TODO: link to parent/child chapter -->
</sect1>
</chapter>