995 lines
41 KiB
XML
995 lines
41 KiB
XML
<chapter id="collections">
|
|
<title>Collection Mapping</title>
|
|
|
|
<sect1 id="collections-persistent" revision="2">
|
|
<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 Java type of a property holding a collection must be the interface type: <literal>Map</literal>,
|
|
<literal>Set</literal>, <literal>List</literal>, or simply <literal>Collection</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>
|
|
|
|
<para>
|
|
You may therefore initialize a collection in your class with whatever implementation
|
|
you find compatible. The persistent collections injected by Hibernate behave like
|
|
<literal>HashMap</literal>,
|
|
<literal>HashSet</literal>,
|
|
<literal>TreeMap</literal>,
|
|
<literal>TreeSet</literal> and
|
|
<literal>ArrayList</literal>
|
|
respectively. Of course, this depends on the mapping style you chose (ie. ordered or
|
|
not, preserving position of elements, allowing duplicates, etc).
|
|
</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 (not the contents) obey the usual rules for value types: no shared
|
|
references, created and deleted along with the owning 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>, on the table holding the collection elements.
|
|
The collection key is mapped by the <literal><key></literal> element. If you
|
|
have a foreign-key constraint set in the database, and have chosen the
|
|
<literal>ON DELETE CASCADE</literal> option, always use the
|
|
<literal>on-delete</literal> attribute on your <literal><key></literal>
|
|
mappings:
|
|
</para>
|
|
|
|
<programlisting><![CDATA[<key column="CHILD_ID" on-delete="cascade"/>]]></programlisting>
|
|
|
|
<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 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 its own lifecycle. In this case, only the "link"
|
|
between two objects is stored in the collection (non-Java developers call these links
|
|
"pointers"). 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>,
|
|
or in the case of entity references, with <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 mappings, except those with set and bag semantics, need an
|
|
<emphasis>index</emphasis> column 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, it may be an entity reference,
|
|
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" revision="2">
|
|
<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"
|
|
fetch="join|select"
|
|
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>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 and fetching by sequential select. 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" revision="1">
|
|
<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="element1" 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="element1b" coords="2 45"/>
|
|
<area id="element2b" coords="3 45"/>
|
|
</areaspec>
|
|
<programlisting><![CDATA[<element
|
|
column="column_name"
|
|
type="typename"
|
|
/>]]></programlisting>
|
|
<calloutlist>
|
|
<callout arearefs="element1b">
|
|
<para>
|
|
<literal>column</literal> (required): The name of the column holding the
|
|
collection element values.
|
|
</para>
|
|
</callout>
|
|
<callout arearefs="element2b">
|
|
<para>
|
|
<literal>type</literal> (required): The type of the collection element.
|
|
</para>
|
|
</callout>
|
|
</calloutlist>
|
|
</programlistingco>
|
|
|
|
<para>
|
|
A collection of entity references, where the references are held in a separate table
|
|
corresponds to the relational notion of <emphasis>many-to-many association</emphasis>.
|
|
</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"
|
|
fetch="select|join"
|
|
/>]]></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>fetch</literal> (optional - defaults to <literal>join</literal>):
|
|
enables outer-join or sequential select fetching for this association. This
|
|
is a special case, for full eager fetching (in a single <literal>SELECT</literal>)
|
|
of an entity and its many-to-many relationships to other entities, you would
|
|
enable <literal>join</literal> fetching not only of the collection itself,
|
|
but also with this attribute on the <literal><many-to-many></literal>
|
|
nested element.
|
|
</para>
|
|
</callout>
|
|
</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-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"/>
|
|
<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="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"/>
|
|
<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">
|
|
<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" 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>
|
|
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, 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="org.hibernate.auction.Category">
|
|
<id name="id" column="ID"/>
|
|
...
|
|
<bag name="items" table="CATEGORY_ITEM">
|
|
<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">
|
|
<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.create(item); // The relationship won't be saved!
|
|
session.create(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!
|
|
</para>
|
|
|
|
<para>
|
|
The same is of course also true for bidirectional one-to-many associations. 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">
|
|
<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 three 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">
|
|
<key column="employer_id"/>
|
|
<index-many-to-many column="employee_id" class="Employee"/>
|
|
<one-to-many class="Contract"/>
|
|
</map>]]></programlisting>
|
|
|
|
<programlisting><![CDATA[<map name="connections">
|
|
<key column="node1_id"/>
|
|
<index-many-to-many column="node2_id" class="Node"/>
|
|
<many-to-many column="connection_id" class="Connection"/>
|
|
</map>]]></programlisting>
|
|
|
|
<para>
|
|
A final alternative is to simply remodel the association as an entity class. This
|
|
is the approach we use most commonly.
|
|
</para>
|
|
|
|
</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" 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>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">
|
|
<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">
|
|
<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" 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>
|
|
|
|
<para>
|
|
For more examples and a complete walk-through a parent/child relationship mapping,
|
|
see <xref linkend="example-parentchild"/>.
|
|
</para>
|
|
|
|
</sect1>
|
|
|
|
</chapter>
|