1075 lines
45 KiB
XML
1075 lines
45 KiB
XML
<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><key></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><element></literal>, <literal><composite-element></literal>,
|
|
<literal><one-to-many></literal>, <literal><many-to-many></literal> or
|
|
<literal><many-to-any></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><index></literal>, <literal><index-many-to-many></literal>,
|
|
<literal><composite-index></literal> or <literal><index-many-to-any></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><set></literal>,
|
|
<literal><list></literal>,
|
|
<literal><map></literal>,
|
|
<literal><bag></literal>,
|
|
<literal><array></literal> and
|
|
<literal><primitive-array></literal> elements.
|
|
<literal><map></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><bag></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><key></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><index></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><index-many-to-many></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><element></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><key></literal> and <literal><index></literal> elements
|
|
described above.
|
|
</para>
|
|
|
|
<para>
|
|
The <literal><one-to-many></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><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 <literal><key></literal>
|
|
column of a <literal><one-to-many></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" 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" lazy="true">
|
|
<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>
|
|
|
|
</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><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">
|
|
<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>
|