[hibernate-commits] Hibernate SVN: r19725 - core/trunk/documentation/manual/src/main/docbook/en-US/content.
hibernate-commits at lists.jboss.org
hibernate-commits at lists.jboss.org
Mon Jun 14 08:34:33 EDT 2010
Author: hardy.ferentschik
Date: 2010-06-14 08:34:32 -0400 (Mon, 14 Jun 2010)
New Revision: 19725
Modified:
core/trunk/documentation/manual/src/main/docbook/en-US/content/collection_mapping.xml
Log:
HHH-5149 Updated collection mapping to take annotatons into consideration
Modified: core/trunk/documentation/manual/src/main/docbook/en-US/content/collection_mapping.xml
===================================================================
--- core/trunk/documentation/manual/src/main/docbook/en-US/content/collection_mapping.xml 2010-06-14 05:28:18 UTC (rev 19724)
+++ core/trunk/documentation/manual/src/main/docbook/en-US/content/collection_mapping.xml 2010-06-14 12:34:32 UTC (rev 19725)
@@ -1,4 +1,4 @@
-<?xml version='1.0' encoding="UTF-8"?>
+<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Hibernate, Relational Persistence for Idiomatic Java
~
@@ -22,140 +22,276 @@
~ 51 Franklin Street, Fifth Floor
~ Boston, MA 02110-1301 USA
-->
-
-<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
+<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
<!ENTITY % BOOK_ENTITIES SYSTEM "../HIBERNATE_-_Relational_Persistence_for_Idiomatic_Java.ent">
%BOOK_ENTITIES;
-
]>
-
<chapter id="collections">
- <title>Collection mapping</title>
+ <title>Collection mapping</title>
- <section id="collections-persistent" revision="3">
- <title>Persistent collections</title>
-
- <para>
- Hibernate requires that persistent collection-valued fields be declared
- as an interface type. For example:
- </para>
-
- <programlisting role="JAVA"><![CDATA[public class Product {
+ <section id="collections-persistent" revision="3">
+ <title>Persistent collections</title>
+
+ <para>Of course Hibernate also allows to persist collections. These
+ persistent collections can contain almost any other Hibernate type,
+ including: basic types, custom types, components and references to other
+ entities. This is an important distinction. An object in a collection
+ might be handled with "value" semantics (its life cycle fully depends on
+ the collection owner), or it might be a reference to another entity with
+ its own life cycle. In the latter case, only the "link" between the two
+ objects is considered to be a state held by the collection.</para>
+
+ <para>As a requirement persistent collection-valued fields must be
+ declared as an interface type. For example:</para>
+
+ <example id="example-collection-values-field">
+ <title>Persistent collection-valued fields must be interfaces</title>
+
+ <programlisting role="JAVA">public class Product {
private String serialNumber;
private Set parts = new HashSet();
public Set getParts() { return parts; }
void setParts(Set parts) { this.parts = parts; }
+
public String getSerialNumber() { return serialNumber; }
void setSerialNumber(String sn) { serialNumber = sn; }
-}]]></programlisting>
-
- <para>
- The actual interface might be <literal>java.util.Set</literal>,
- <literal>java.util.Collection</literal>, <literal>java.util.List</literal>,
- <literal>java.util.Map</literal>, <literal>java.util.SortedSet</literal>,
- <literal>java.util.SortedMap</literal> or anything you like
- ("anything you like" means you will have to write an implementation of
- <literal>org.hibernate.usertype.UserCollectionType</literal>.)
- </para>
-
- <para>
- Notice how the instance variable was initialized with an instance of
- <literal>HashSet</literal>. This is the best way to initialize collection
- valued properties of newly instantiated (non-persistent) instances. When
- you make the instance persistent, by calling <literal>persist()</literal>
- for example, Hibernate will actually replace the <literal>HashSet</literal>
- with an instance of Hibernate's own implementation of <literal>Set</literal>.
- Be aware of the following errors:
- </para>
-
- <programlisting role="JAVA"><![CDATA[Cat cat = new DomesticCat();
+}</programlisting>
+ </example>
+
+ <para>The actual interface might be <literal>java.util.Set</literal>,
+ <literal>java.util.Collection</literal>,
+ <literal>java.util.List</literal>, <literal>java.util.Map</literal>,
+ <literal>java.util.SortedSet</literal>,
+ <literal>java.util.SortedMap</literal> or anything you like ("anything you
+ like" means you will have to write an implementation of
+ <literal>org.hibernate.usertype.UserCollectionType</literal>).</para>
+
+ <para>Notice how in <xref linkend="example-collection-values-field" /> the
+ instance variable <literal>parts</literal> was initialized with an
+ instance of <literal>HashSet</literal>. This is the best way to initialize
+ collection valued properties of newly instantiated (non-persistent)
+ instances. When you make the instance persistent, by calling
+ <literal>persist()</literal>, Hibernate will actually replace the
+ <literal>HashSet</literal> with an instance of Hibernate's own
+ implementation of <literal>Set</literal>. Be aware of the following
+ error:</para>
+
+ <example>
+ <title>Hibernate uses its own collection implementations</title>
+
+ <programlisting role="JAVA">Cat cat = new DomesticCat();
Cat kitten = new DomesticCat();
....
Set kittens = new HashSet();
kittens.add(kitten);
cat.setKittens(kittens);
session.persist(cat);
+
kittens = cat.getKittens(); // Okay, kittens collection is a Set
-(HashSet) cat.getKittens(); // Error!]]></programlisting>
+(HashSet) cat.getKittens(); // Error!</programlisting>
+ </example>
- <para>
- The persistent collections injected by Hibernate behave like
- <literal>HashMap</literal>, <literal>HashSet</literal>,
- <literal>TreeMap</literal>, <literal>TreeSet</literal> or
- <literal>ArrayList</literal>, depending on the interface type.
- </para>
+ <para>The persistent collections injected by Hibernate behave like
+ <literal>HashMap</literal>, <literal>HashSet</literal>,
+ <literal>TreeMap</literal>, <literal>TreeSet</literal> or
+ <literal>ArrayList</literal>, depending on the interface type.</para>
- <para>
- Collections instances have the usual behavior of value types. They are
- automatically persisted when referenced by a persistent object and
- are automatically deleted when unreferenced. If a collection is passed from one
- persistent object to another, its elements might be moved from one table to
- another. Two entities cannot share a reference to the same collection
- instance. Due to the underlying relational model, collection-valued properties
- do not support null value semantics. Hibernate does not distinguish between
- a null collection reference and an empty collection.
- </para>
+ <para>Collections instances have the usual behavior of value types. They
+ are automatically persisted when referenced by a persistent object and are
+ automatically deleted when unreferenced. If a collection is passed from
+ one persistent object to another, its elements might be moved from one
+ table to another. Two entities cannot share a reference to the same
+ collection instance. Due to the underlying relational model,
+ collection-valued properties do not support null value semantics.
+ Hibernate does not distinguish between a null collection reference and an
+ empty collection.</para>
- <para>
- Use persistent collections
- the same way you use ordinary Java collections. However, please ensure you understand
- the semantics of bidirectional associations (these are discussed later).
- </para>
+ <para>Use persistent collections the same way you use ordinary Java
+ collections. However, please ensure you understand the semantics of
+ bidirectional associations (see <xref
+ linkend="collections-bidirectional" />).</para>
+ </section>
- </section>
+ <section id="collections-mapping" revision="4">
+ <title>Collection mappings</title>
- <section id="collections-mapping" revision="4">
- <title>Collection mappings</title>
+ <para>Using annotations you can map <classname>Collection</classname>s,
+ <classname>Lists</classname>, <classname>Maps</classname> and
+ <classname>Sets</classname> of associated entities using @OneToMany and
+ @ManyToMany. For collections of a basic or embeddable type use
+ @ElementCollection. In the simplest case a collection mapping looks like
+ this:</para>
- <tip>
- <para>
- There are quite a range of mappings that can be generated for collections that cover
- many common relational models. We suggest you experiment with the schema generation tool
- so that you understand how various mapping declarations translate to database tables.
- </para>
- </tip>
+ <example>
+ <title>Collection mapping using annotations</title>
- <para>
- The Hibernate mapping element used for mapping a collection depends upon
- the type of interface. For example, a <literal><set></literal>
- element is used for mapping properties of type <literal>Set</literal>.
- </para>
-
- <programlisting role="JAVA"><![CDATA[<class name="Product">
- <id name="serialNumber" column="productSerialNumber"/>
- <set name="parts">
- <key column="productSerialNumber" not-null="true"/>
- <one-to-many class="Part"/>
- </set>
-</class>]]></programlisting>
+ <programlisting role="JAVA">@Entity
+public class Product {
- <para>
- Apart from <literal><set></literal>, there is also
- <literal><list></literal>, <literal><map></literal>,
- <literal><bag></literal>, <literal><array></literal> and
- <literal><primitive-array></literal> mapping elements. The
- <literal><map></literal> element is representative:
- </para>
+ private String serialNumber;
+ private Set<Part> parts = new HashSet<Part>();
- <programlistingco role="XML">
- <areaspec>
- <area id="mappingcollection1" coords="2"/>
- <area id="mappingcollection2" coords="3"/>
- <area id="mappingcollection3" coords="4"/>
- <area id="mappingcollection4" coords="5"/>
- <area id="mappingcollection5" coords="6"/>
- <area id="mappingcollection6" coords="7"/>
- <area id="mappingcollection7" coords="8"/>
- <area id="mappingcollection8" coords="9"/>
- <area id="mappingcollection9" coords="10"/>
- <area id="mappingcollection10" coords="11"/>
- <area id="mappingcollection11" coords="12"/>
- <area id="mappingcollection12" coords="13"/>
- <area id="mappingcollection13" coords="14"/>
- <area id="mappingcollection14" coords="15"/>
- </areaspec>
- <programlisting><![CDATA[<map
+ @Id
+ public String getSerialNumber() { return serialNumber; }
+ void setSerialNumber(String sn) { serialNumber = sn; }
+
+ @OneToMany
+ public Set<Part> getParts() { return parts; }
+ void setParts(Set parts) { this.parts = parts; }
+}
+
+
+ at Entity
+public class Part {
+ ...
+}</programlisting>
+ </example>
+
+ <para>The <literal>@OneToMany</literal> and <literal>@ManyToOne</literal>
+ annotations have several options and there are other annotations which can
+ be used in in combination with these annotations. But lets first see how
+ collections are mapped using Hibernate mapping files. Here the situation
+ is more complex than in the annotation case, since the mapping element
+ used for mapping a collection depends upon the type of interface. For
+ example, a <literal><set></literal> element is used for mapping
+ properties of type <literal>Set</literal>.</para>
+
+ <example floatstyle="" id="example.collections.set">
+ <title>Mapping a Set using <set></title>
+
+ <programlisting role="JAVA"><class name="Product">
+ <id name="serialNumber" column="productSerialNumber"/>
+ <set name="parts">
+ <key column="productSerialNumber" not-null="true"/>
+ <one-to-many class="Part"/>
+ </set>
+</class></programlisting>
+ </example>
+
+ <para>In <xref linkend="example.collections.set" /> a
+ <emphasis>one-to-many association</emphasis> links the
+ <literal>Product</literal> and <literal>Part</literal> entities. This
+ association requires the existence of a foreign key column and possibly an
+ index column to the <literal>Part</literal> table. This mapping loses
+ certain semantics of normal Java collections:</para>
+
+ <itemizedlist spacing="compact">
+ <listitem>
+ <para>An instance of the contained entity class cannot belong to more
+ than one instance of the collection.</para>
+ </listitem>
+
+ <listitem>
+ <para>An instance of the contained entity class cannot appear at more
+ than one value of the collection index.</para>
+ </listitem>
+ </itemizedlist>
+
+ <para>The <literal><one-to-many></literal> tag has the following
+ options.</para>
+
+ <example>
+ <title>options of <one-to-many> tag</title>
+
+ <programlistingco role="XML">
+ <areaspec>
+ <area coords="2" id="onetomany1" />
+
+ <area coords="3" id="onetomany2" />
+
+ <area coords="4" id="onetomany3" />
+ </areaspec>
+
+ <programlisting><one-to-many
+ class="ClassName"
+ not-found="ignore|exception"
+ entity-name="EntityName"
+ node="element-name"
+ embed-xml="true|false"
+ /></programlisting>
+
+ <calloutlist>
+ <callout arearefs="onetomany1">
+ <para><literal>class</literal> (required): the name of the
+ associated class.</para>
+ </callout>
+
+ <callout arearefs="onetomany2">
+ <para><literal>not-found</literal> (optional - defaults to
+ <literal>exception</literal>): specifies how cached identifiers
+ that reference missing rows will be handled.
+ <literal>ignore</literal> will treat a missing row as a null
+ association.</para>
+ </callout>
+
+ <callout arearefs="onetomany3">
+ <para><literal>entity-name</literal> (optional): the entity name
+ of the associated class, as an alternative to
+ <literal>class</literal>.</para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+ </example>
+
+ <para>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>
+
+ <warning>
+ <para>If the foreign key column of a
+ <literal><one-to-many></literal> association is declared
+ <literal>NOT NULL</literal>, you must declare the
+ <literal><key></literal> mapping
+ <literal>not-null="true"</literal> or <emphasis>use a bidirectional
+ association</emphasis> with the collection mapping marked
+ <literal>inverse="true"</literal>. See the discussion of bidirectional
+ associations later in this chapter for more information.</para>
+ </warning>
+
+ <para>Apart from the <literal><set> </literal>tag as shown in <xref
+ linkend="example.collections.set" />, there is also
+ <literal><list></literal>, <literal><map></literal>,
+ <literal><bag></literal>, <literal><array></literal> and
+ <literal><primitive-array></literal> mapping elements. The
+ <literal><map></literal> element is representative:</para>
+
+ <example>
+ <title>Elements of the <map> mapping</title>
+
+ <programlistingco role="XML">
+ <areaspec>
+ <area coords="2" id="mappingcollection1" />
+
+ <area coords="3" id="mappingcollection2" />
+
+ <area coords="4" id="mappingcollection3" />
+
+ <area coords="5" id="mappingcollection4" />
+
+ <area coords="6" id="mappingcollection5" />
+
+ <area coords="7" id="mappingcollection6" />
+
+ <area coords="8" id="mappingcollection7" />
+
+ <area coords="9" id="mappingcollection8" />
+
+ <area coords="10" id="mappingcollection9" />
+
+ <area coords="11" id="mappingcollection10" />
+
+ <area coords="12" id="mappingcollection11" />
+
+ <area coords="13" id="mappingcollection12" />
+
+ <area coords="14" id="mappingcollection13" />
+
+ <area coords="15" id="mappingcollection14" />
+ </areaspec>
+
+ <programlisting><map
name="propertyName"
table="table_name"
schema="schema_name"
@@ -172,300 +308,660 @@
mutable="true|false"
node="element-name|."
embed-xml="true|false"
->
+>
- <key .... />
- <map-key .... />
- <element .... />
-</map>]]></programlisting>
- <calloutlist>
- <callout arearefs="mappingcollection1">
- <para>
- <literal>name</literal>: the collection property name
- </para>
- </callout>
- <callout arearefs="mappingcollection2">
- <para>
- <literal>table</literal> (optional - defaults to property name): the
- name of the collection table. It is 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>):
- disables lazy fetching and specifies that the association is
- always eagerly fetched. It can also be used to enable "extra-lazy" fetching where most
- operations do not initialize the collection. This is suitable for large
- collections.
- </para>
- </callout>
- <callout arearefs="mappingcollection5">
- <para>
- <literal>inverse</literal> (optional - defaults to <literal>false</literal>):
- marks 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>):
- enables operations to cascade to child entities.
- </para>
- </callout>
- <callout arearefs="mappingcollection7">
- <para>
- <literal>sort</literal> (optional): specifies 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): specifies 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): specifies an arbitrary SQL <literal>WHERE</literal>
- condition that is used when retrieving or removing the collection. This is useful if the
- collection needs to contain only a subset of the available data.
- </para>
- </callout>
- <callout arearefs="mappingcollection10">
- <para>
- <literal>fetch</literal> (optional, defaults to <literal>select</literal>): chooses
- between outer-join fetching, fetching by sequential select, and fetching by sequential
- subselect.
- </para>
- </callout>
- <callout arearefs="mappingcollection11">
- <para>
- <literal>batch-size</literal> (optional, defaults to <literal>1</literal>): specifies 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 uses for accessing the collection property value.
- </para>
- </callout>
- <callout arearefs="mappingcollection13">
- <para>
- <literal>optimistic-lock</literal> (optional - defaults to <literal>true</literal>):
- specifies that changes to the state of the collection results in increments of the
- owning entity's version. For one-to-many associations you may want to
- disable this setting.
- </para>
- </callout>
- <callout arearefs="mappingcollection14">
- <para>
- <literal>mutable</literal> (optional - defaults to <literal>true</literal>):
- a value of <literal>false</literal> specifies that the elements of the
- collection never change. This allows for minor performance optimization in some cases.
- </para>
- </callout>
- </calloutlist>
- </programlistingco>
+ <key .... />
+ <map-key .... />
+ <element .... />
+</map></programlisting>
- <section id="collections-foreignkeys" >
- <title>Collection foreign keys</title>
-
- <para>
- Collection instances are distinguished in the database by the foreign key of
- the entity that owns the collection. This foreign key is referred to as the
- <emphasis>collection key column</emphasis>, or columns, of the collection
- table. The collection key column is mapped by the <literal><key></literal>
- element.
- </para>
-
- <para>
- There can be a nullability constraint on the foreign key column. For most
- collections, this is implied. For unidirectional one-to-many associations,
- the foreign key column is nullable by default, so you may need to specify
- <literal>not-null="true"</literal>.
- </para>
-
- <programlisting role="XML"><![CDATA[<key column="productSerialNumber" not-null="true"/>]]></programlisting>
-
- <para>
- The foreign key constraint can use <literal>ON DELETE CASCADE</literal>.
- </para>
-
- <programlisting role="XML"><![CDATA[<key column="productSerialNumber" on-delete="cascade"/>]]></programlisting>
-
- <para>
- See the previous chapter for a full definition of the <literal><key></literal>
- element.
- </para>
-
- </section>
-
- <section id="collections-elements" >
- <title>Collection elements</title>
-
- <para>
- Collections can contain almost any other Hibernate type, including: basic types,
- custom types, components and references to other entities. This is an
- important distinction. An object in a collection might be handled with "value"
- semantics (its life cycle fully depends on the collection owner), or it might be a
- reference to another entity with its own life cycle. In the latter case, only the
- "link" between the two objects is considered to be a state held by the collection.
- </para>
-
- <para>
- The contained type is referred to as the <emphasis>collection element type</emphasis>.
- Collection elements are mapped by <literal><element></literal> or
- <literal><composite-element></literal>, or in the case of entity references,
- with <literal><one-to-many></literal> or <literal><many-to-many></literal>.
- The first two map elements with value semantics, the next two are used to map entity
- associations.
- </para>
-
- </section>
-
- <section id="collections-indexed">
- <title>Indexed collections</title>
-
- <para>
- All collection mappings, except those with set and bag semantics, need an
- <emphasis>index column</emphasis> in the collection table. An index column is a column that maps to an
- array index, or <literal>List</literal> index, or <literal>Map</literal> key. The
- index of a <literal>Map</literal> may be of any basic type, mapped with
- <literal><map-key></literal>. It can be an entity reference mapped with
- <literal><map-key-many-to-many></literal>, or it can be a composite type
- mapped with <literal><composite-map-key></literal>. The index of an array or
- list is always of type <literal>integer</literal> and is mapped using the
- <literal><list-index></literal> element. The mapped column contains
- sequential integers that are numbered from zero by default.
- </para>
+ <calloutlist>
+ <callout arearefs="mappingcollection1">
+ <para><literal>name</literal>: the collection property name</para>
+ </callout>
- <programlistingco role="XML">
+ <callout arearefs="mappingcollection2">
+ <para><literal>table</literal> (optional - defaults to property
+ name): the name of the collection table. It is 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>): disables lazy fetching and specifies
+ that the association is always eagerly fetched. It can also be
+ used to enable "extra-lazy" fetching where most operations do not
+ initialize the collection. This is suitable for large
+ collections.</para>
+ </callout>
+
+ <callout arearefs="mappingcollection5">
+ <para><literal>inverse</literal> (optional - defaults to
+ <literal>false</literal>): marks 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>): enables operations to cascade to child
+ entities.</para>
+ </callout>
+
+ <callout arearefs="mappingcollection7">
+ <para><literal>sort</literal> (optional): specifies 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): specifies 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): specifies an arbitrary
+ SQL <literal>WHERE</literal> condition that is used when
+ retrieving or removing the collection. This is useful if the
+ collection needs to contain only a subset of the available
+ data.</para>
+ </callout>
+
+ <callout arearefs="mappingcollection10">
+ <para><literal>fetch</literal> (optional, defaults to
+ <literal>select</literal>): chooses between outer-join fetching,
+ fetching by sequential select, and fetching by sequential
+ subselect.</para>
+ </callout>
+
+ <callout arearefs="mappingcollection11">
+ <para><literal>batch-size</literal> (optional, defaults to
+ <literal>1</literal>): specifies 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 uses for
+ accessing the collection property value.</para>
+ </callout>
+
+ <callout arearefs="mappingcollection13">
+ <para><literal>optimistic-lock</literal> (optional - defaults to
+ <literal>true</literal>): specifies that changes to the state of
+ the collection results in increments of the owning entity's
+ version. For one-to-many associations you may want to disable this
+ setting.</para>
+ </callout>
+
+ <callout arearefs="mappingcollection14">
+ <para><literal>mutable</literal> (optional - defaults to
+ <literal>true</literal>): a value of <literal>false</literal>
+ specifies that the elements of the collection never change. This
+ allows for minor performance optimization in some cases.</para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+ </example>
+
+ <section id="collections-foreignkeys">
+ <title>Collection foreign keys</title>
+
+ <para>Collection instances are distinguished in the database by the
+ foreign key of the entity that owns the collection. This foreign key is
+ referred to as the <emphasis>collection key column</emphasis>, or
+ columns, of the collection table. The collection key column is mapped by
+ the <literal>@JoinColumn</literal> annotation respectively the
+ <literal><key></literal> XML element.</para>
+
+ <para>There can be a nullability constraint on the foreign key column.
+ For most collections, this is implied. For unidirectional one-to-many
+ associations, the foreign key column is nullable by default, so you may
+ need to specify</para>
+
+ <programlisting role="Java">@JoinColumn(nullable=false)</programlisting>
+
+ <para>or</para>
+
+ <programlisting role="XML"><key column="productSerialNumber" not-null="true"/></programlisting>
+
+ <para>The foreign key constraint can use <literal>ON DELETE
+ CASCADE</literal>. In XML this can be expressed via:</para>
+
+ <programlisting role="XML"><key column="productSerialNumber" on-delete="cascade"/></programlisting>
+
+ <para>In annotations the Hibernate specific annotation @OnDelete has to
+ be used.</para>
+
+ <programlisting role="Java">@OnDelete(action=OnDeleteAction.CASCADE)</programlisting>
+
+ <para>See <xref linkend="section-key" /> for more information about the
+ <literal><key></literal> element.</para>
+ </section>
+
+ <section id="collections-indexed">
+ <title id="section.indexed.collections">Indexed collections</title>
+
+ <section>
+ <title>Lists</title>
+
+ <para>Lists can be mapped in two different ways:</para>
+
+ <itemizedlist>
+ <listitem>
+ <para>as ordered lists, where the order is not materialized in the
+ database</para>
+ </listitem>
+
+ <listitem>
+ <para>as indexed lists, where the order is materialized in the
+ database</para>
+ </listitem>
+ </itemizedlist>
+
+ <para>To order lists in memory, add
+ <literal>@javax.persistence.OrderBy</literal> to your property. This
+ annotation takes as parameter a list of comma separated properties (of
+ the target entity) and order the collection accordingly (eg
+ <code>firstname asc, age desc</code>), if the string is empty, the
+ collection will be ordered by the primary key of the target
+ entity.</para>
+
+ <example>
+ <title>Ordered lists using <classname>@OrderBy</classname></title>
+
+ <programlisting language="JAVA" role="JAVA">@Entity
+public class Customer {
+ @Id @GeneratedValue public Integer getId() { return id; }
+ public void setId(Integer id) { this.id = id; }
+ private Integer id;
+
+ @OneToMany(mappedBy="customer")
+ @OrderBy("number")
+ public List<Order> getOrders() { return orders; }
+ public void setOrders(List<Order> orders) { this.orders = orders; }
+ private List<Order> orders;
+}
+
+ at Entity
+public class Order {
+ @Id @GeneratedValue public Integer getId() { return id; }
+ public void setId(Integer id) { this.id = id; }
+ private Integer id;
+
+ public String getNumber() { return number; }
+ public void setNumber(String number) { this.number = number; }
+ private String number;
+
+ @ManyToOne
+ public Customer getCustomer() { return customer; }
+ public void setCustomer(Customer customer) { this.customer = customer; }
+ private Customer number;
+}
+
+-- Table schema
+|-------------| |----------|
+| Order | | Customer |
+|-------------| |----------|
+| id | | id |
+| number | |----------|
+| customer_id |
+|-------------|</programlisting>
+ </example>
+
+ <para>To store the index value in a dedicated column, use the
+ <classname>@javax.persistence.OrderColumn</classname> annotation on
+ your property. This annotations describes the column name and
+ attributes of the column keeping the index value. This column is
+ hosted on the table containing the association foreign key. If the
+ column name is not specified, the default is the name of the
+ referencing property, followed by underscore, followed by
+ <literal>ORDER</literal> (in the following example, it would be
+ <literal>orders_ORDER</literal>).</para>
+
+ <example>
+ <title>Additional index column using
+ <classname>@OrderColumn</classname></title>
+
+ <programlisting language="JAVA" role="JAVA">@Entity
+public class Customer {
+ @Id @GeneratedValue public Integer getId() { return id; }
+ public void setId(Integer id) { this.id = id; }
+ private Integer id;
+
+ @OneToMany(mappedBy="customer")
+ @OrderColumn(name="orders_index")
+ public List<Order> getOrders() { return orders; }
+ public void setOrders(List<Order> orders) { this.orders = orders; }
+ private List<Order> orders;
+}
+
+ at Entity
+public class Order {
+ @Id @GeneratedValue public Integer getId() { return id; }
+ public void setId(Integer id) { this.id = id; }
+ private Integer id;
+
+ public String getNumber() { return number; }
+ public void setNumber(String number) { this.number = number; }
+ private String number;
+
+ @ManyToOne
+ public Customer getCustomer() { return customer; }
+ public void setCustomer(Customer customer) { this.customer = customer; }
+ private Customer number;
+}
+
+-- Table schema
+|--------------| |----------|
+| Order | | Customer |
+|--------------| |----------|
+| id | | id |
+| number | |----------|
+| customer_id |
+| orders_order |
+|--------------|</programlisting>
+ </example>
+
+ <note>
+ <para>We recommend you to convert <classname>the legacy
+ @org.hibernate.annotations.IndexColumn</classname> usages to
+ <classname>@OrderColumn</classname> unless you are making use of the
+ base property. The <literal>base</literal> property lets you define
+ the index value of the first element (aka as base index). The usual
+ value is <literal>0</literal> or <literal>1</literal>. The default
+ is 0 like in Java.</para>
+ </note>
+
+ <para>Using mapping files the index of an array or list is always of
+ type <literal>integer</literal> and is mapped using the
+ <literal><list-index></literal> element. The mapped column
+ contains sequential integers that are numbered from zero by
+ default.</para>
+
+ <example>
+ <title>index-list element for indexed collections in xml
+ mapping</title>
+
+ <programlistingco role="XML">
<areaspec>
- <area id="index1" coords="2"/>
- <area id="index2" coords="3"/>
- </areaspec>
- <programlisting><![CDATA[<list-index
+ <area coords="2" id="index1" />
+
+ <area coords="3" id="index2" />
+ </areaspec>
+
+ <programlisting><list-index
column="column_name"
- base="0|1|..."/>]]></programlisting>
+ base="0|1|..."/></programlisting>
+
<calloutlist>
- <callout arearefs="index1">
- <para>
- <literal>column_name</literal> (required): the name of the column holding the
- collection index values.
- </para>
- </callout>
- <callout arearefs="index1">
- <para>
- <literal>base</literal> (optional - defaults to <literal>0</literal>): the value
- of the index column that corresponds to the first element of the list or array.
- </para>
- </callout>
+ <callout arearefs="index1">
+ <para><literal>column_name</literal> (required): the name of
+ the column holding the collection index values.</para>
+ </callout>
+
+ <callout arearefs="index1">
+ <para><literal>base</literal> (optional - defaults to
+ <literal>0</literal>): the value of the index column that
+ corresponds to the first element of the list or array.</para>
+ </callout>
</calloutlist>
- </programlistingco>
+ </programlistingco>
+ </example>
- <programlistingco role="XML">
+ <para>If your table does not have an index column, and you still wish
+ to use <literal>List</literal> as the property type, you can map the
+ property as a Hibernate <emphasis><bag></emphasis>. A bag does
+ not retain its order when it is retrieved from the database, but it
+ can be optionally sorted or ordered.</para>
+ </section>
+
+ <section>
+ <title>Maps</title>
+
+ <para>Maps can borrow their keys from one of the associated entity
+ properties or have dedicated columns to store an explicit key.</para>
+
+ <para>To use one of the target entity property as a key of the map,
+ use <literal>@MapKey(name="myProperty")</literal>
+ (<literal>myProperty</literal> is a property name in the target
+ entity). When using <literal>@MapKey</literal> (without property
+ name), the target entity primary key is used. The map key uses the
+ same column as the property pointed out: there is no additional column
+ defined to hold the map key, and it does make sense since the map key
+ actually represent a target property. Be aware that once loaded, the
+ key is no longer kept in sync with the property, in other words, if
+ you change the property value, the key will not change automatically
+ in your Java model.</para>
+
+ <example>
+ <title>Use of target entity property as map key via
+ <classname>@MapKey</classname></title>
+
+ <programlisting language="JAVA" role="JAVA">@Entity
+public class Customer {
+ @Id @GeneratedValue public Integer getId() { return id; }
+ public void setId(Integer id) { this.id = id; }
+ private Integer id;
+
+ @OneToMany(mappedBy="customer")
+ @MapKey(name="number")
+ public Map<String,Order> getOrders() { return orders; }
+ public void setOrders(Map<String,Order> order) { this.orders = orders; }
+ private Map<String,Order> orders;
+}
+
+ at Entity
+public class Order {
+ @Id @GeneratedValue public Integer getId() { return id; }
+ public void setId(Integer id) { this.id = id; }
+ private Integer id;
+
+ public String getNumber() { return number; }
+ public void setNumber(String number) { this.number = number; }
+ private String number;
+
+ @ManyToOne
+ public Customer getCustomer() { return customer; }
+ public void setCustomer(Customer customer) { this.customer = customer; }
+ private Customer number;
+}
+
+-- Table schema
+|-------------| |----------|
+| Order | | Customer |
+|-------------| |----------|
+| id | | id |
+| number | |----------|
+| customer_id |
+|-------------|</programlisting>
+ </example>
+
+ <para>Otherwise, the map key is mapped to a dedicated column or
+ columns. In order to customize the mapping use one of the following
+ annotations:</para>
+
+ <itemizedlist>
+ <listitem>
+ <para>@<classname>MapKeyColumn</classname> if the map key is a
+ basic type, if you don't specify the column name, the name of the
+ property followed by underscore followed by <literal>KEY</literal>
+ is used (for example <literal>orders_KEY</literal>).</para>
+ </listitem>
+
+ <listitem>
+ <para><classname>@MapKeyEnumerated</classname> /
+ <classname>@MapKeyTemporal</classname> if the map key type is
+ respectively an enum or a <classname>Date</classname>.</para>
+ </listitem>
+
+ <listitem>
+ <para><classname>@MapKeyJoinColumn</classname>/<classname>@MapKeyJoinColumns</classname>
+ if the map key type is another entity.</para>
+ </listitem>
+
+ <listitem>
+ <para><classname>@AttributeOverride</classname>/<classname>@AttributeOverrides</classname>
+ when the map key is a embeddable object. Use
+ <literal>key.</literal> as a prefix for your embeddable object
+ property names.</para>
+ </listitem>
+ </itemizedlist>
+
+ <para>You can also use <classname>@MapKeyClass</classname> to define
+ the type of the key if you don't use generics.</para>
+
+ <example>
+ <title>Map key as basic type using
+ <classname>@MapKeyColumn</classname></title>
+
+ <programlisting language="JAVA" role="JAVA">@Entity
+public class Customer {
+ @Id @GeneratedValue public Integer getId() { return id; }
+ public void setId(Integer id) { this.id = id; }
+ private Integer id;
+
+ @OneToMany @JoinTable(name="Cust_Order")
+ @MapKeyColumn(name="orders_number")
+ public Map<String,Order> getOrders() { return orders; }
+ public void setOrders(Map<String,Order> orders) { this.orders = orders; }
+ private Map<String,Order> orders;
+}
+
+ at Entity
+public class Order {
+ @Id @GeneratedValue public Integer getId() { return id; }
+ public void setId(Integer id) { this.id = id; }
+ private Integer id;
+
+ public String getNumber() { return number; }
+ public void setNumber(String number) { this.number = number; }
+ private String number;
+
+ @ManyToOne
+ public Customer getCustomer() { return customer; }
+ public void setCustomer(Customer customer) { this.customer = customer; }
+ private Customer number;
+}
+
+-- Table schema
+|-------------| |----------| |---------------|
+| Order | | Customer | | Cust_Order |
+|-------------| |----------| |---------------|
+| id | | id | | customer_id |
+| number | |----------| | order_id |
+| customer_id | | orders_number |
+|-------------| |---------------|</programlisting>
+ </example>
+
+ <note>
+ <para>We recommend you to migrate from
+ <classname>@org.hibernate.annotations.MapKey</classname> /
+ <classname>@org.hibernate.annotation.MapKeyManyToMany</classname> to
+ the new standard approach described above</para>
+ </note>
+
+ <para>Using Hibernate mapping files there exists similar concepts
+ using <literal><map-key></literal>,
+ <literal><map-key-many-to-many></literal> and
+ <literal><composite-map-key></literal> which are even a little
+ more flexible since SQL expressions can be used to define the map key.
+ <literal><map-key></literal> is used for any basic type,
+ <literal><map-key-many-to-many></literal> for an entity
+ reference and <literal><composite-map-key></literal> for a
+ composite type.</para>
+
+ <example>
+ <title>map-key xml mapping element</title>
+
+ <programlistingco role="XML">
<areaspec>
- <area id="mapkey1" coords="2"/>
- <area id="mapkey2" coords="3"/>
- <area id="mapkey3" coords="4"/>
- </areaspec>
- <programlisting><![CDATA[<map-key
+ <area coords="2" id="mapkey1" />
+
+ <area coords="3" id="mapkey2" />
+
+ <area coords="4" id="mapkey3" />
+ </areaspec>
+
+ <programlisting><map-key
column="column_name"
formula="any SQL expression"
type="type_name"
node="@attribute-name"
- length="N"/>]]></programlisting>
+ length="N"/></programlisting>
+
<calloutlist>
- <callout arearefs="mapkey1">
- <para>
- <literal>column</literal> (optional): the name of the column holding the
- collection index values.
- </para>
- </callout>
- <callout arearefs="mapkey2">
- <para>
- <literal>formula</literal> (optional): a SQL formula used to evaluate the
- key of the map.
- </para>
- </callout>
- <callout arearefs="mapkey3">
- <para>
- <literal>type</literal> (required): the type of the map keys.
- </para>
- </callout>
+ <callout arearefs="mapkey1">
+ <para><literal>column</literal> (optional): the name of the
+ column holding the collection index values.</para>
+ </callout>
+
+ <callout arearefs="mapkey2">
+ <para><literal>formula</literal> (optional): a SQL formula
+ used to evaluate the key of the map.</para>
+ </callout>
+
+ <callout arearefs="mapkey3">
+ <para><literal>type</literal> (required): the type of the map
+ keys.</para>
+ </callout>
</calloutlist>
- </programlistingco>
+ </programlistingco>
+ </example>
- <programlistingco role="XML">
+ <example>
+ <title>map-key-many-to-many</title>
+
+ <programlistingco role="XML">
<areaspec>
- <area id="indexmanytomany1" coords="2"/>
- <area id="indexmanytomany2" coords="3"/>
- <area id="indexmanytomany3" coords="3"/>
- </areaspec>
- <programlisting><![CDATA[<map-key-many-to-many
+ <area coords="2" id="indexmanytomany1" />
+
+ <area coords="3" id="indexmanytomany2" />
+
+ <area coords="3" id="indexmanytomany3" />
+ </areaspec>
+
+ <programlisting><map-key-many-to-many
column="column_name"
formula="any SQL expression"
class="ClassName"
-/>]]></programlisting>
+/></programlisting>
+
<calloutlist>
- <callout arearefs="indexmanytomany1">
- <para>
- <literal>column</literal> (optional): the name of the foreign key
- column for the collection index values.
- </para>
- </callout>
- <callout arearefs="indexmanytomany2">
- <para>
- <literal>formula</literal> (optional): a SQ formula used to evaluate the
- foreign key of the map key.
- </para>
- </callout>
- <callout arearefs="indexmanytomany3">
- <para>
- <literal>class</literal> (required): the entity class used as the map key.
- </para>
- </callout>
- </calloutlist>
- </programlistingco>
+ <callout arearefs="indexmanytomany1">
+ <para><literal>column</literal> (optional): the name of the
+ foreign key column for the collection index values.</para>
+ </callout>
+ <callout arearefs="indexmanytomany2">
+ <para><literal>formula</literal> (optional): a SQ formula used
+ to evaluate the foreign key of the map key.</para>
+ </callout>
- <para>
- If your table does not have an index column, and you still wish to use <literal>List</literal>
- as the property type, you can map the property as a Hibernate <emphasis><bag></emphasis>.
- A bag does not retain its order when it is retrieved from the database, but it can be
- optionally sorted or ordered.
- </para>
-
- </section>
+ <callout arearefs="indexmanytomany3">
+ <para><literal>class</literal> (required): the entity class
+ used as the map key.</para>
+ </callout>
+ </calloutlist>
+ </programlistingco>
+ </example>
+ </section>
+ </section>
<section id="collections-ofvalues" revision="2">
- <title>Collections of values and many-to-many associations</title>
+ <title>Collections of values</title>
- <para>
- Any collection of values or many-to-many associations requires a dedicated
- <emphasis>collection table</emphasis> with a foreign key column or columns,
- <emphasis>collection element column</emphasis> or columns, and possibly
- an index column or columns.
- </para>
+ <para>In some situations you don't need to associate two entities but
+ simply create a collection of basic types or embeddable objects. Use the
+ <classname>@ElementCollection</classname> in this case.</para>
- <para>
- For a collection of values use the <literal><element></literal> tag. For example:
- </para>
+ <example>
+ <title>Collection of basic types mapped via
+ <classname>@ElementCollection</classname></title>
+ <programlisting language="JAVA" role="JAVA">@Entity
+public class User {
+ [...]
+ public String getLastname() { ...}
+
+ @ElementCollection
+ @CollectionTable(name="Nicknames", joinColumns=@JoinColumn(name="user_id"))
+ @Column(name="nickname")
+ public Set<String> getNicknames() { ... }
+}</programlisting>
+ </example>
+
+ <para>The collection table holding the collection data is set using the
+ <classname>@CollectionTable</classname> annotation. If omitted the
+ collection table name default to the concatenation of the name of the
+ containing entity and the name of the collection attribute, separated by
+ an underscore. In our example, it would be
+ <literal>User_nicknames</literal>.</para>
+
+ <para>The column holding the basic type is set using the
+ <classname>@Column</classname> annotation. If omitted, the column name
+ defaults to the property name: in our example, it would be
+ <literal>nicknames</literal>.</para>
+
+ <para>But you are not limited to basic types, the collection type can be
+ any embeddable object. To override the columns of the embeddable object
+ in the collection table, use the
+ <classname>@AttributeOverride</classname> annotation.</para>
+
+ <example>
+ <title>@ElementCollection for embeddable objects</title>
+
+ <programlisting language="JAVA" role="JAVA">@Entity
+public class User {
+ [...]
+ public String getLastname() { ...}
+
+ @ElementCollection
+ @CollectionTable(name="Addresses", joinColumns=@JoinColumn(name="user_id"))
+ @AttributeOverrides({
+ @AttributeOverride(name="street1", column=@Column(name="fld_street"))
+ })
+ public Set<Address> getAddresses() { ... }
+}
+
+ at Embeddable
+public class Address {
+ public String getStreet1() {...}
+ [...]
+}</programlisting>
+ </example>
+
+ <para>Such an embeddable object cannot contains a collection
+ itself.</para>
+
+ <note>
+ <para>in <classname>@AttributeOverride</classname>, you must use the
+ <literal>value.</literal> prefix to override properties of the
+ embeddable object used in the map value and the
+ <literal>key.</literal> prefix to override properties of the
+ embeddable object used in the map key.</para>
+
+ <programlisting language="JAVA" role="JAVA">@Entity
+public class User {
+ @ElementCollection
+ @AttributeOverrides({
+ @AttributeOverride(name="key.street1", column=@Column(name="fld_street")),
+ @AttributeOverride(name="value.stars", column=@Column(name="fld_note"))
+ })
+ public Map<Address,Rating> getFavHomes() { ... }</programlisting>
+ </note>
+
+ <note>
+ <para>We recommend you to migrate from
+ <classname>@org.hibernate.annotations.CollectionOfElements</classname>
+ to the new <classname>@ElementCollection</classname>
+ annotation.</para>
+ </note>
+
+ <para>Using the mapping file approach a collection of values is mapped
+ using the <literal><element></literal> tag. For example:</para>
+
+ <example>
+ <title><element> tag for collection values using mapping
+ files</title>
+
<programlistingco role="XML">
- <areaspec>
- <area id="element1b" coords="2"/>
- <area id="element2b" coords="3"/>
- <area id="element3b" coords="4"/>
- </areaspec>
- <programlisting><![CDATA[<element
+ <areaspec>
+ <area coords="2" id="element1b" />
+
+ <area coords="3" id="element2b" />
+
+ <area coords="4" id="element3b" />
+ </areaspec>
+
+ <programlisting><element
column="column_name"
formula="any SQL expression"
type="typename"
@@ -475,612 +971,575 @@
not-null="true|false"
unique="true|false"
node="element-name"
-/>]]></programlisting>
- <calloutlist>
- <callout arearefs="element1b">
- <para>
- <literal>column</literal> (optional): the name of the column holding the
- collection element values.
- </para>
- </callout>
- <callout arearefs="element2b">
- <para>
- <literal>formula</literal> (optional): an SQL formula used to evaluate the
- element.
- </para>
- </callout>
- <callout arearefs="element3b">
- <para>
- <literal>type</literal> (required): the type of the collection element.
- </para>
- </callout>
- </calloutlist>
- </programlistingco>
+/></programlisting>
- <para>
- A <emphasis>many-to-many association</emphasis> is specified using the
- <literal><many-to-many></literal> element.
- </para>
+ <calloutlist>
+ <callout arearefs="element1b">
+ <para><literal>column</literal> (optional): the name of the
+ column holding the collection element values.</para>
+ </callout>
- <programlistingco role="XML">
- <areaspec>
- <area id="manytomany1" coords="2"/>
- <area id="manytomany2" coords="3"/>
- <area id="manytomany3" coords="4"/>
- <area id="manytomany4" coords="5"/>
- <area id="manytomany5" coords="6"/>
- <area id="manytomany6" coords="7"/>
- <area id="manytomany7" coords="8"/>
- <area id="manytomany8" coords="9"/>
- </areaspec>
- <programlisting><![CDATA[<many-to-many
- column="column_name"
- formula="any SQL expression"
- class="ClassName"
- fetch="select|join"
- unique="true|false"
- not-found="ignore|exception"
- entity-name="EntityName"
- property-ref="propertyNameFromAssociatedClass"
- node="element-name"
- embed-xml="true|false"
- />]]></programlisting>
- <calloutlist>
- <callout arearefs="manytomany1">
- <para>
- <literal>column</literal> (optional): the name of the element foreign key column.
- </para>
- </callout>
- <callout arearefs="manytomany2">
- <para>
- <literal>formula</literal> (optional): an SQL formula used to evaluate the element
- foreign key value.
- </para>
- </callout>
- <callout arearefs="manytomany3">
- <para>
- <literal>class</literal> (required): the name of the associated class.
- </para>
- </callout>
- <callout arearefs="manytomany4">
- <para>
- <literal>fetch</literal> (optional - defaults to <literal>join</literal>):
- enables outer-join or sequential select fetching for this association. This
- is a special case; for full eager fetching in a single <literal>SELECT</literal>
- of an entity and its many-to-many relationships to other entities, you would
- enable <literal>join</literal> fetching,not only of the collection itself,
- but also with this attribute on the <literal><many-to-many></literal>
- nested element.
- </para>
- </callout>
- <callout arearefs="manytomany5">
- <para>
- <literal>unique</literal> (optional): enables the DDL generation of a unique
- constraint for the foreign-key column. This makes the association multiplicity
- effectively one-to-many.
- </para>
- </callout>
- <callout arearefs="manytomany6">
- <para>
- <literal>not-found</literal> (optional - defaults to <literal>exception</literal>):
- specifies how foreign keys that reference missing rows will be handled:
- <literal>ignore</literal> will treat a missing row as a null association.
- </para>
- </callout>
- <callout arearefs="manytomany7">
- <para>
- <literal>entity-name</literal> (optional): the entity name of the associated class,
- as an alternative to <literal>class</literal>.
- </para>
- </callout>
- <callout arearefs="manytomany8">
- <para>
- <literal>property-ref</literal> (optional): the name of a property of the associated
- class that is joined to this foreign key. If not specified, the primary key of
- the associated class is used.
- </para>
- </callout>
- </calloutlist>
+ <callout arearefs="element2b">
+ <para><literal>formula</literal> (optional): an SQL formula used
+ to evaluate the element.</para>
+ </callout>
+
+ <callout arearefs="element3b">
+ <para><literal>type</literal> (required): the type of the
+ collection element.</para>
+ </callout>
+ </calloutlist>
</programlistingco>
+ </example>
+ </section>
+ </section>
- <para>
- Here are some examples.
- </para>
- <para>
- A set of strings:
- </para>
+ <section id="collections-advancedmappings">
+ <title>Advanced collection mappings</title>
- <programlisting role="XML"><![CDATA[<set name="names" table="person_names">
- <key column="person_id"/>
- <element column="person_name" type="string"/>
-</set>]]></programlisting>
+ <section id="collections-sorted" revision="2">
+ <title id="section.sorted.collections">Sorted collections</title>
- <para>
- A bag containing integers with an iteration order determined by the
- <literal>order-by</literal> attribute:
- </para>
+ <para>Hibernate supports collections implementing
+ <literal>java.util.SortedMap</literal> and
+ <literal>java.util.SortedSet</literal>. With annotations you declare a
+ sort comparator using <literal>@Sort</literal>. You between the
+ comparator types unsorted, natural or custom. If you want to use your
+ own comparator implementation, you'll also have to express the
+ implementation class using the <literal>comparator</literal> attribute.
+ Note that you need to use either a <classname>SortedSet</classname> or a
+ <classname>SortedMap</classname> interface.</para>
- <programlisting role="XML"><![CDATA[<bag name="sizes"
- table="item_sizes"
- order-by="size asc">
- <key column="item_id"/>
- <element column="size" type="integer"/>
-</bag>]]></programlisting>
+ <example>
+ <title>Sorted collection with @Sort</title>
- <para>
- An array of entities, in this case, a many-to-many association:
- </para>
+ <programlisting language="JAVA" role="JAVA">@OneToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER)
+ at JoinColumn(name="CUST_ID")
+ at Sort(type = SortType.COMPARATOR, comparator = TicketComparator.class)
+ at Where(clause="1=1")
+ at OnDelete(action=OnDeleteAction.CASCADE)
+public SortedSet<Ticket> getTickets() {
+ return tickets;
+}</programlisting>
+ </example>
- <programlisting role="XML"><![CDATA[<array name="addresses"
- table="PersonAddress"
- cascade="persist">
- <key column="personId"/>
- <list-index column="sortOrder"/>
- <many-to-many column="addressId" class="Address"/>
-</array>]]></programlisting>
+ <para>Using Hibernate mapping files you specify a comparator in the
+ mapping file with <literal><sort></literal>:</para>
- <para>
- A map from string indices to dates:
- </para>
+ <example>
+ <title>Sorted collection using xml mapping</title>
- <programlisting role="XML"><![CDATA[<map name="holidays"
- table="holidays"
- schema="dbo"
- order-by="hol_name asc">
- <key column="id"/>
- <map-key column="hol_name" type="string"/>
- <element column="hol_date" type="date"/>
-</map>]]></programlisting>
+ <programlisting role="XML"><set name="aliases"
+ table="person_aliases"
+ sort="natural">
+ <key column="person"/>
+ <element column="name" type="string"/>
+</set>
- <para>
- A list of components (this is discussed in the next chapter):
- </para>
+<map name="holidays" sort="my.custom.HolidayComparator">
+ <key column="year_id"/>
+ <map-key column="hol_name" type="string"/>
+ <element column="hol_date" type="date"/>
+</map></programlisting>
+ </example>
- <programlisting role="XML"><![CDATA[<list name="carComponents"
- table="CarComponents">
- <key column="carId"/>
- <list-index column="sortOrder"/>
- <composite-element class="CarComponent">
- <property name="price"/>
- <property name="type"/>
- <property name="serialNumber" column="serialNum"/>
- </composite-element>
-</list>]]></programlisting>
+ <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 and is implemented using
+ <literal>LinkedHashSet</literal> or <literal>LinkedHashMap</literal>.
+ This performs the ordering in the SQL query and not in the
+ memory.</para>
+
+ <example>
+ <title>Sorting in database using order-by</title>
+
+ <programlisting role="XML"><set name="aliases" table="person_aliases" order-by="lower(name) asc">
+ <key column="person"/>
+ <element column="name" type="string"/>
+</set>
+
+<map name="holidays" order-by="hol_date, hol_name">
+ <key column="year_id"/>
+ <map-key column="hol_name" type="string"/>
+ <element column="hol_date type="date"/>
+</map></programlisting>
+ </example>
+
+ <note>
+ <title>Note</title>
+
+ <para>The value of the <literal>order-by</literal> attribute is an SQL
+ ordering, not an HQL ordering.</para>
+ </note>
+
+ <para>Associations can even be sorted by arbitrary criteria at runtime
+ using a collection <literal>filter()</literal>:</para>
+
+ <example>
+ <title>Sorting via a query filter</title>
+
+ <programlisting role="JAVA">sortedUsers = s.createFilter( group.getUsers(), "order by this.name" ).list();</programlisting>
+ </example>
</section>
- <section id="collections-onetomany">
- <title>One-to-many associations</title>
+ <section id="collections-bidirectional" revision="1">
+ <title>Bidirectional associations</title>
- <para>
- A <emphasis>one-to-many association</emphasis> links the tables of two classes
- via a foreign key with no intervening collection table. This mapping loses
- certain semantics of normal Java collections:
- </para>
+ <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>
- <itemizedlist spacing="compact">
<listitem>
- <para>
- An instance of the contained entity class cannot belong to more than
- one instance of the collection.
- </para>
+ <para>set or bag valued at one end and single-valued at the
+ other</para>
</listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>many-to-many</term>
+
<listitem>
- <para>
- An instance of the contained entity class cannot appear at more than
- one value of the collection index.
- </para>
+ <para>set or bag valued at both ends</para>
</listitem>
- </itemizedlist>
+ </varlistentry>
+ </variablelist></para>
- <para>
- An association from <literal>Product</literal> to <literal>Part</literal> requires
- the existence of a foreign key column and possibly an index column to the <literal>Part</literal>
- table. A <literal><one-to-many></literal> tag indicates that this is a one-to-many
- association.
- </para>
+ <para>Often there exists a many to one association which is the owner
+ side of a bidirectional relationship. The corresponding one to many
+ association is in this case annotated by
+ <literal>@OneToMany(mappedBy=...)</literal></para>
- <programlistingco role="XML">
- <areaspec>
- <area id="onetomany1" coords="2"/>
- <area id="onetomany2" coords="3"/>
- <area id="onetomany3" coords="4"/>
- </areaspec>
- <programlisting><![CDATA[<one-to-many
- class="ClassName"
- not-found="ignore|exception"
- entity-name="EntityName"
- node="element-name"
- embed-xml="true|false"
- />]]></programlisting>
- <calloutlist>
- <callout arearefs="onetomany1">
- <para>
- <literal>class</literal> (required): the name of the associated class.
- </para>
- </callout>
- <callout arearefs="onetomany2">
- <para>
- <literal>not-found</literal> (optional - defaults to <literal>exception</literal>):
- specifies how cached identifiers that reference missing rows will be handled.
- <literal>ignore</literal> will treat a missing row as a null association.
- </para>
- </callout>
- <callout arearefs="onetomany3">
- <para>
- <literal>entity-name</literal> (optional): the entity name of the associated class,
- as an alternative to <literal>class</literal>.
- </para>
- </callout>
- </calloutlist>
- </programlistingco>
-
- <para>
- 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>
+ <example>
+ <title>Bidirectional one to many with many to one side as association
+ owner</title>
- <warning>
- <para>
- If the foreign key column of a
- <literal><one-to-many></literal> association is declared <literal>NOT NULL</literal>,
- you must declare the <literal><key></literal> mapping
- <literal>not-null="true"</literal> or <emphasis>use a bidirectional association</emphasis>
- with the collection mapping marked <literal>inverse="true"</literal>. See the discussion
- of bidirectional associations later in this chapter for more information.
- </para>
- </warning>
-
- <para>
- The following example shows a map of <literal>Part</literal> entities by name, where
- <literal>partName</literal> is a persistent property of <literal>Part</literal>.
- Notice the use of a formula-based index:
- </para>
+ <programlisting language="JAVA" role="JAVA">@Entity
+public class Troop {
+ @OneToMany(mappedBy="troop")
+ public Set<Soldier> getSoldiers() {
+ ...
+}
- <programlisting role="XML"><![CDATA[<map name="parts"
- cascade="all">
- <key column="productId" not-null="true"/>
- <map-key formula="partName"/>
- <one-to-many class="Part"/>
-</map>]]></programlisting>
- </section>
-
- </section>
+ at Entity
+public class Soldier {
+ @ManyToOne
+ @JoinColumn(name="troop_fk")
+ public Troop getTroop() {
+ ...
+} </programlisting>
+ </example>
- <section id="collections-advancedmappings">
- <title>Advanced collection mappings</title>
+ <para><classname>Troop</classname> has a bidirectional one to many
+ relationship with <literal>Soldier</literal> through the
+ <literal>troop</literal> property. You don't have to (must not) define
+ any physical mapping in the <literal>mappedBy</literal> side.</para>
- <section id="collections-sorted" revision="2">
- <title>Sorted collections</title>
+ <para>To map a bidirectional one to many, with the one-to-many side as
+ the owning side, you have to remove the <literal>mappedBy</literal>
+ element and set the many to one <literal>@JoinColumn</literal> as
+ insertable and updatable to false. This solution is not optimized and
+ will produce additional UPDATE statements.</para>
- <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>
+ <example>
+ <title>Bidirectional associtaion with one to many side as
+ owner</title>
- <programlisting role="XML"><![CDATA[<set name="aliases"
- table="person_aliases"
- sort="natural">
- <key column="person"/>
- <element column="name" type="string"/>
-</set>
+ <programlisting language="JAVA" role="JAVA">@Entity
+public class Troop {
+ @OneToMany
+ @JoinColumn(name="troop_fk") //we need to duplicate the physical information
+ public Set<Soldier> getSoldiers() {
+ ...
+}
-<map name="holidays" sort="my.custom.HolidayComparator">
- <key column="year_id"/>
- <map-key column="hol_name" type="string"/>
- <element column="hol_date" type="date"/>
-</map>]]></programlisting>
+ at Entity
+public class Soldier {
+ @ManyToOne
+ @JoinColumn(name="troop_fk", insertable=false, updatable=false)
+ public Troop getTroop() {
+ ...
+}</programlisting>
+ </example>
- <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>Again a look at the maping file approach. There you can define a
+ bidirectional one-to-many association by mapping a one-to-many
+ association to the same table column(s) as a many-to-one association and
+ declaring the many-valued end <literal>inverse="true"</literal>.</para>
- <para>
- Sorted collections actually behave like <literal>java.util.TreeSet</literal> or
- <literal>java.util.TreeMap</literal>.
- </para>
+ <example>
+ <title>Bidirectional one to many via Hibernate mapping files</title>
- <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 and is implemented using <literal>LinkedHashSet</literal> or
- <literal>LinkedHashMap</literal>. This performs the ordering in the SQL query and
- not in the memory.
- </para>
+ <programlisting role="XML"><class name="Parent">
+ <id name="id" column="parent_id"/>
+ ....
+ <set name="children" inverse="true">
+ <key column="parent_id"/>
+ <one-to-many class="Child"/>
+ </set>
+</class>
- <programlisting role="XML"><![CDATA[<set name="aliases" table="person_aliases" order-by="lower(name) asc">
- <key column="person"/>
- <element column="name" type="string"/>
-</set>
+<class name="Child">
+ <id name="id" column="child_id"/>
+ ....
+ <many-to-one name="parent"
+ class="Parent"
+ column="parent_id"
+ not-null="true"/>
+</class></programlisting>
+ </example>
-<map name="holidays" order-by="hol_date, hol_name">
- <key column="year_id"/>
- <map-key column="hol_name" type="string"/>
- <element column="hol_date type="date"/>
-</map>]]></programlisting>
+ <para>Mapping one end of an association with
+ <literal>inverse="true"</literal> does not affect the operation of
+ cascades as these are orthogonal concepts.</para>
- <note>
- <title>Note</title>
- <para>
- The value of the <literal>order-by</literal> attribute is an SQL ordering, not
- an HQL ordering.
- </para>
- </note>
+ <para>A many-to-many association is defined logically using the
+ <literal>@ManyToMany</literal> annotation. You also have to describe the
+ association table and the join conditions using the
+ <literal>@JoinTable</literal> annotation. If the association is
+ bidirectional, one side has to be the owner and one side has to be the
+ inverse end (ie. it will be ignored when updating the relationship
+ values in the association table):</para>
- <para>
- Associations can even be sorted by arbitrary criteria at runtime using a collection
- <literal>filter()</literal>:
- </para>
+ <example>
+ <title>Many to many association via @ManyToMany</title>
- <programlisting role="JAVA"><![CDATA[sortedUsers = s.createFilter( group.getUsers(), "order by this.name" ).list();]]></programlisting>
+ <programlisting language="JAVA" role="JAVA">@Entity
+public class Employer implements Serializable {
+ @ManyToMany(
+ targetEntity=org.hibernate.test.metadata.manytomany.Employee.class,
+ cascade={CascadeType.PERSIST, CascadeType.MERGE}
+ )
+ @JoinTable(
+ name="EMPLOYER_EMPLOYEE",
+ joinColumns=@JoinColumn(name="EMPER_ID"),
+ inverseJoinColumns=@JoinColumn(name="EMPEE_ID")
+ )
+ public Collection getEmployees() {
+ return employees;
+ }
+ ...
+} </programlisting>
- </section>
+ <programlisting language="JAVA" role="JAVA">@Entity
+public class Employee implements Serializable {
+ @ManyToMany(
+ cascade = {CascadeType.PERSIST, CascadeType.MERGE},
+ mappedBy = "employees",
+ targetEntity = Employer.class
+ )
+ public Collection getEmployers() {
+ return employers;
+ }
+} </programlisting>
+ </example>
- <section id="collections-bidirectional" revision="1">
- <title>Bidirectional associations</title>
+ <para>We've already shown the many declarations and the detailed
+ attributes for associations. We'll go deeper in the
+ <literal>@JoinTable</literal> description, it defines a
+ <literal>name</literal>, an array of join columns, and an array of
+ inverse join columns. The latter ones are the columns of the association
+ table which refer to the <classname>Employee</classname> primary key
+ (the "other side"). As seen previously, the other side don't have to
+ (must not) describe the physical mapping: a simple
+ <literal>mappedBy</literal> argument containing the owner side property
+ name bind the two.</para>
- <para>
- A <emphasis>bidirectional association</emphasis> allows navigation from both
- "ends" of the association. Two kinds of bidirectional association are
- supported:
+ <para>As any other annotations, most values are guessed in a many to
+ many relationship. Without describing any physical mapping in a
+ unidirectional many to many the following rules applied. The table name
+ is the concatenation of the owner table name, <keycap>_</keycap> and the
+ other side table name. The foreign key name(s) referencing the owner
+ table is the concatenation of the owner table name, <keycap>_</keycap>
+ and the owner primary key column(s). The foreign key name(s) referencing
+ the other side is the concatenation of the owner property name,
+ <keycap>_</keycap>, and the other side primary key column(s). These are
+ the same rules used for a unidirectional one to many
+ relationship.</para>
- <variablelist>
- <varlistentry>
- <term>one-to-many</term>
- <listitem>
- <para>
- set or bag valued at one end and 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>
+ <example>
+ <title>Default values for <classname>@ManyToMany</classname>
+ (uni-directional)</title>
- </para>
+ <programlisting language="JAVA" role="JAVA">@Entity
+public class Store {
+ @ManyToMany(cascade = CascadeType.PERSIST)
+ public Set<City> getImplantedIn() {
+ ...
+ }
+}
- <para>
- You can specify a bidirectional many-to-many association by mapping two
- many-to-many associations to the same database table and declaring one end as
- <emphasis>inverse</emphasis>. You cannot select an
- indexed collection.
- </para>
+ at Entity
+public class City {
+ ... //no bidirectional relationship
+} </programlisting>
+ </example>
- <para>
- Here is an example of a bidirectional many-to-many association that illustrates how each category can
- have many items and each item can be in many categories:
- </para>
+ <para>A <literal>Store_City</literal> is used as the join table. The
+ <literal>Store_id</literal> column is a foreign key to the
+ <literal>Store</literal> table. The <literal>implantedIn_id</literal>
+ column is a foreign key to the <literal>City</literal> table.</para>
- <programlisting role="XML"><![CDATA[<class name="Category">
- <id name="id" column="CATEGORY_ID"/>
+ <para>Without describing any physical mapping in a bidirectional many to
+ many the following rules applied. The table name is the concatenation of
+ the owner table name, <keycap>_</keycap> and the other side table name.
+ The foreign key name(s) referencing the owner table is the concatenation
+ of the other side property name, <keycap>_</keycap>, and the owner
+ primary key column(s). The foreign key name(s) referencing the other
+ side is the concatenation of the owner property name,
+ <keycap>_</keycap>, and the other side primary key column(s). These are
+ the same rules used for a unidirectional one to many
+ relationship.</para>
+
+ <example>
+ <title>Default values for <classname>@ManyToMany</classname>
+ (bi-directional)</title>
+
+ <programlisting language="JAVA" role="JAVA">@Entity
+public class Store {
+ @ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
+ public Set<Customer> getCustomers() {
+ ...
+ }
+}
+
+ at Entity
+public class Customer {
+ @ManyToMany(mappedBy="customers")
+ public Set<Store> getStores() {
+ ...
+ }
+} </programlisting>
+ </example>
+
+ <para>A <literal>Store_Customer</literal> is used as the join table. The
+ <literal>stores_id</literal> column is a foreign key to the
+ <literal>Store</literal> table. The <literal>customers_id</literal>
+ column is a foreign key to the <literal>Customer</literal> table.</para>
+
+ <para>Using Hibernate mapping files you can map a bidirectional
+ many-to-many association by mapping two many-to-many associations to the
+ same database table and declaring one end as
+ <emphasis>inverse</emphasis>. <note>
+ <para>You cannot select an indexed collection.</para>
+ </note></para>
+
+ <para>Here is an example of a bidirectional many-to-many association
+ that illustrates how each category can have many items and each item can
+ be in many categories:</para>
+
+ <example>
+ <title>Many to many association using Hibernate mapping files</title>
+
+ <programlisting role="XML"><class name="Category">
+ <id name="id" column="CATEGORY_ID"/>
...
- <bag name="items" table="CATEGORY_ITEM">
- <key column="CATEGORY_ID"/>
- <many-to-many class="Item" column="ITEM_ID"/>
- </bag>
-</class>
+ <bag name="items" table="CATEGORY_ITEM">
+ <key column="CATEGORY_ID"/>
+ <many-to-many class="Item" column="ITEM_ID"/>
+ </bag>
+</class>
-<class name="Item">
- <id name="id" column="ITEM_ID"/>
+<class name="Item">
+ <id name="id" column="ITEM_ID"/>
...
- <!-- inverse end -->
- <bag name="categories" table="CATEGORY_ITEM" inverse="true">
- <key column="ITEM_ID"/>
- <many-to-many class="Category" column="CATEGORY_ID"/>
- </bag>
-</class>]]></programlisting>
+ <!-- inverse end -->
+ <bag name="categories" table="CATEGORY_ITEM" inverse="true">
+ <key column="ITEM_ID"/>
+ <many-to-many class="Category" column="CATEGORY_ID"/>
+ </bag>
+</class></programlisting>
+ </example>
- <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
- a many-to-many relationship in Javais created:
- </para>
+ <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 a many-to-many
+ relationship in Javais created:</para>
- <programlisting role="JAVA"><![CDATA[
-category.getItems().add(item); // The category now "knows" about the relationship
-item.getCategories().add(category); // The item now "knows" about the relationship
+ <example>
+ <title>Effect of inverse vs. non-inverse side of many to many
+ associations</title>
+ <programlisting role="JAVA">category.getItems().add(item); // The category now "knows" about the relationship
+item.getCategories().add(category); // The item now "knows" about the relationship
+
session.persist(item); // The relationship won't be saved!
-session.persist(category); // The relationship will be saved]]></programlisting>
+session.persist(category); // The relationship will be saved</programlisting>
+ </example>
- <para>
- The non-inverse side is used to save the in-memory representation to the database.
- </para>
+ <para>The non-inverse side is used to save the in-memory representation
+ to the database.</para>
+ </section>
- <para>
- You can define a bidirectional one-to-many association by mapping a one-to-many association
- to the same table column(s) as a many-to-one association and declaring the many-valued
- end <literal>inverse="true"</literal>.
- </para>
+ <section id="collections-indexedbidirectional">
+ <title>Bidirectional associations with indexed collections</title>
- <programlisting role="XML"><![CDATA[<class name="Parent">
- <id name="id" column="parent_id"/>
- ....
- <set name="children" inverse="true">
- <key column="parent_id"/>
- <one-to-many class="Child"/>
- </set>
-</class>
+ <para>Here are some additional consideration for bidirectional mappings
+ with indexed collections using Hibernate mapping files. Refer also to
+ <xref linkend="section.indexed.collections" /> and <xref
+ linkend="section.sorted.collections" />.</para>
-<class name="Child">
- <id name="id" column="child_id"/>
- ....
- <many-to-one name="parent"
- class="Parent"
- column="parent_id"
- not-null="true"/>
-</class>]]></programlisting>
+ <para>A bidirectional association where one end is represented as a
+ <literal><list></literal> or <literal><map></literal>,
+ requires special consideration. If there is a property of the child
+ class that maps to the index column you can use
+ <literal>inverse="true"</literal> on the collection mapping:</para>
- <para>
- Mapping one end of an association with <literal>inverse="true"</literal> does not
- affect the operation of cascades as these are orthogonal concepts.
- </para>
+ <example>
+ <title>Bidirectional association with indexed collection</title>
- </section>
-
- <section id="collections-indexedbidirectional">
- <title>Bidirectional associations with indexed collections</title>
- <para>
- A bidirectional association where one end is represented as a <literal><list></literal>
- or <literal><map></literal>, requires special consideration. If there is a property of
- the child class that maps to the index column you can use
- <literal>inverse="true"</literal> on the collection mapping:
- </para>
-
- <programlisting role="XML"><![CDATA[<class name="Parent">
- <id name="id" column="parent_id"/>
+ <programlisting role="XML"><class name="Parent">
+ <id name="id" column="parent_id"/>
....
- <map name="children" inverse="true">
- <key column="parent_id"/>
- <map-key column="name"
- type="string"/>
- <one-to-many class="Child"/>
- </map>
-</class>
+ <map name="children" inverse="true">
+ <key column="parent_id"/>
+ <map-key column="name"
+ type="string"/>
+ <one-to-many class="Child"/>
+ </map>
+</class>
-<class name="Child">
- <id name="id" column="child_id"/>
+<class name="Child">
+ <id name="id" column="child_id"/>
....
- <property name="name"
- not-null="true"/>
- <many-to-one name="parent"
+ <property name="name"
+ not-null="true"/>
+ <many-to-one name="parent"
class="Parent"
column="parent_id"
- not-null="true"/>
-</class>]]></programlisting>
+ not-null="true"/>
+</class></programlisting>
+ </example>
- <para>
- If there is no such property on the child class, the association cannot be considered
- truly bidirectional. That is, there is information available at one end of the association that is
- not available at the other end. In this case, you cannot map the collection
- <literal>inverse="true"</literal>. Instead, you could use the following mapping:
- </para>
+ <para>If there is no such property on the child class, the association
+ cannot be considered truly bidirectional. That is, there is information
+ available at one end of the association that is not available at the
+ other end. In this case, you cannot map the collection
+ <literal>inverse="true"</literal>. Instead, you could use the following
+ mapping:</para>
- <programlisting role="XML"><![CDATA[<class name="Parent">
- <id name="id" column="parent_id"/>
+ <example>
+ <title>Bidirectional association with indexed collection, but no index
+ column</title>
+
+ <programlisting role="XML"><class name="Parent">
+ <id name="id" column="parent_id"/>
....
- <map name="children">
- <key column="parent_id"
- not-null="true"/>
- <map-key column="name"
- type="string"/>
- <one-to-many class="Child"/>
- </map>
-</class>
+ <map name="children">
+ <key column="parent_id"
+ not-null="true"/>
+ <map-key column="name"
+ type="string"/>
+ <one-to-many class="Child"/>
+ </map>
+</class>
-<class name="Child">
- <id name="id" column="child_id"/>
+<class name="Child">
+ <id name="id" column="child_id"/>
....
- <many-to-one name="parent"
+ <many-to-one name="parent"
class="Parent"
column="parent_id"
insert="false"
update="false"
- not-null="true"/>
-</class>]]></programlisting>
+ not-null="true"/>
+</class></programlisting>
+ </example>
- <para>
- Note that in this mapping, the collection-valued end of the association is responsible for
- updates to the foreign key. <!--TODO: Does this really result in some unnecessary update statements?-->
- </para>
+ <para>Note that in this mapping, the collection-valued end of the
+ association is responsible for updates to the foreign key.<!--TODO: Does this really result in some unnecessary update statements?--></para>
+ </section>
- </section>
-
<section id="collections-ternary">
- <title>Ternary associations</title>
+ <title>Ternary associations</title>
- <para>
- There are three possible approaches to mapping a ternary association. One approach is to use a
- <literal>Map</literal> with an association as its index:
- </para>
+ <para>There are three possible approaches to mapping a ternary
+ association. One approach is to use a <literal>Map</literal> with an
+ association as its index:</para>
- <programlisting role="XML"><![CDATA[<map name="contracts">
- <key column="employer_id" not-null="true"/>
- <map-key-many-to-many column="employee_id" class="Employee"/>
- <one-to-many class="Contract"/>
-</map>]]></programlisting>
-
- <programlisting role="XML"><![CDATA[<map name="connections">
- <key column="incoming_node_id"/>
- <map-key-many-to-many column="outgoing_node_id" class="Node"/>
- <many-to-many column="connection_id" class="Connection"/>
-</map>]]></programlisting>
-
- <para>
- A second approach is to remodel the association as an entity class. This
- is the most common approach.
- </para>
-
- <para>
- A final alternative is to use composite elements, which will be discussed later.
- </para>
-
+ <example>
+ <title>Ternary association mapping</title>
+
+ <programlisting role="XML">@Entity
+public class Company {
+ @Id
+ int id;
+ ...
+ @OneToMany // unidirectional
+ @MapKeyJoinColumn(name="employee_id")
+ Map<Employee, Contract> contracts;
+}
+
+// or
+
+<map name="contracts">
+ <key column="employer_id" not-null="true"/>
+ <map-key-many-to-many column="employee_id" class="Employee"/>
+ <one-to-many class="Contract"/>
+</map></programlisting>
+ </example>
+
+ <para>A second approach is to remodel the association as an entity
+ class. This is the most common approach. A final alternative is to use
+ composite elements, which will be discussed later.</para>
</section>
-
+
<section id="collections-idbag" revision="1">
- <title><literal>Using an <idbag></literal></title>
+ <title><literal>Using an <idbag></literal></title>
- <para>
- The majority of the many-to-many associations and collections of values
- shown previously all map to tables with composite keys, even though it has been have suggested
- that entities should have synthetic identifiers (surrogate keys). A
- pure association table does not seem to benefit much from
- a surrogate key, although a collection of composite values <emphasis>might</emphasis>.
- It is for this reason that 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 majority of the many-to-many associations and collections of
+ values shown previously all map to tables with composite keys, even
+ though it has been have suggested that entities should have synthetic
+ identifiers (surrogate keys). A pure association table does not seem to
+ benefit much from a surrogate key, although a collection of composite
+ values <emphasis>might</emphasis>. It is for this reason 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. For example:
- </para>
+ <para>The <literal><idbag></literal> element lets you map a
+ <literal>List</literal> (or <literal>Collection</literal>) with bag
+ semantics. For example:</para>
-<programlisting role="XML"><![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="Person" fetch="join"/>
-</idbag>]]></programlisting>
+ <programlisting role="XML"><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="Person" fetch="join"/>
+</idbag></programlisting>
- <para>
- 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, however, provide any mechanism for discovering the surrogate key value
- of a particular row.
- </para>
+ <para>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, however, provide any mechanism for
+ discovering the surrogate key value of a particular row.</para>
- <para>
- The update performance of an <literal><idbag></literal> supersedes a regular <literal><bag></literal>.
- Hibernate can locate individual rows efficiently and update or delete them
- individually, similar to a list, map or set.
- </para>
+ <para>The update performance of an <literal><idbag></literal>
+ supersedes a regular <literal><bag></literal>. Hibernate can
+ locate individual rows efficiently and update or delete them
+ individually, similar to 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>
-
+ <para>In the current implementation, the <literal>native</literal>
+ identifier generation strategy is not supported for
+ <literal><idbag></literal> collection identifiers.</para>
</section>
+ </section>
- </section>
-
- <!--undocumenting this stuff -->
-
- <!--section id="collections-heterogeneous">
+ <!--undocumenting this stuff -->
+
+ <!--section id="collections-heterogeneous">
<title>Heterogeneous Associations</title>
<para>
@@ -1092,182 +1551,319 @@
</section-->
- <section id="collections-example" revision="1">
- <title>Collection examples</title>
+ <section id="collections-example" revision="1">
+ <title>Collection examples</title>
- <para>
- This section covers collection examples.
- </para>
+ <para>This section covers collection examples.</para>
- <para>
- The following class has a collection of <literal>Child</literal> instances:
- </para>
+ <para>The following class has a collection of <literal>Child</literal>
+ instances:</para>
- <programlisting role="JAVA"><![CDATA[package eg;
-import java.util.Set;
+ <example>
+ <title>Example classes <classname>Parent</classname> and
+ <classname>Child</classname></title>
-public class Parent {
+ <programlisting role="JAVA">public class Parent {
private long id;
- private Set children;
+ private Set<Child> children;
- public long getId() { return id; }
- private void setId(long id) { this.id=id; }
+ // getter/setter
+ ...
+}
- private Set getChildren() { return children; }
- private void setChildren(Set children) { this.children=children; }
- ....
- ....
-}]]></programlisting>
+public class Child {
+ private long id;
+ private String name
- <para>
- If each child has, at most, one parent, the most natural mapping is a
- one-to-many association:
-
- </para>
+
+ // getter/setter
+ ...
+}</programlisting>
+ </example>
- <programlisting role="XML"><![CDATA[<hibernate-mapping>
+ <para>If each child has, at most, one parent, the most natural mapping is
+ a one-to-many association:</para>
- <class name="Parent">
- <id name="id">
- <generator class="sequence"/>
- </id>
- <set name="children">
- <key column="parent_id"/>
- <one-to-many class="Child"/>
- </set>
- </class>
+ <example>
+ <title>One to many unidirectional <classname>Parent-Child</classname>
+ relationship using annotations</title>
- <class name="Child">
- <id name="id">
- <generator class="sequence"/>
- </id>
- <property name="name"/>
- </class>
+ <programlisting role="XML">public class Parent {
+ @Id
+ @GeneratedValue
+ private long id;
-</hibernate-mapping>]]></programlisting>
+ @OneToMany
+ private Set<Child> children;
- <para>
- This maps to the following table definitions:
- </para>
+ // getter/setter
+ ...
+}
- <programlisting role="XML"><![CDATA[create table parent ( id bigint not null primary key )
+
+public class Child {
+ @Id
+ @GeneratedValue
+ private long id;
+ private String name;
+
+
+ // getter/setter
+ ...
+}</programlisting>
+ </example>
+
+ <example>
+ <title>One to many unidirectional <classname>Parent-Child</classname>
+ relationship using mapping files</title>
+
+ <programlisting role="XML"><hibernate-mapping>
+
+ <class name="Parent">
+ <id name="id">
+ <generator class="sequence"/>
+ </id>
+ <set name="children">
+ <key column="parent_id"/>
+ <one-to-many class="Child"/>
+ </set>
+ </class>
+
+ <class name="Child">
+ <id name="id">
+ <generator class="sequence"/>
+ </id>
+ <property name="name"/>
+ </class>
+
+</hibernate-mapping></programlisting>
+ </example>
+
+ <para>This maps to the following table definitions:</para>
+
+ <example>
+ <title>Table definitions for unidirectional
+ <classname>Parent</classname>-<classname>Child</classname>
+ relationship</title>
+
+ <programlisting role="XML">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>
+alter table child add constraint childfk0 (parent_id) references parent</programlisting>
+ </example>
- <para>
- If the parent is <emphasis>required</emphasis>, use a bidirectional one-to-many
- association:
- </para>
+ <para>If the parent is <emphasis>required</emphasis>, use a bidirectional
+ one-to-many association:</para>
- <programlisting role="XML"><![CDATA[<hibernate-mapping>
+ <example>
+ <title>One to many bidirectional <classname>Parent-Child</classname>
+ relationship using annotations</title>
- <class name="Parent">
- <id name="id">
- <generator class="sequence"/>
- </id>
- <set name="children" inverse="true">
- <key column="parent_id"/>
- <one-to-many class="Child"/>
- </set>
- </class>
+ <programlisting role="XML">public class Parent {
+ @Id
+ @GeneratedValue
+ private long id;
- <class name="Child">
- <id name="id">
- <generator class="sequence"/>
- </id>
- <property name="name"/>
- <many-to-one name="parent" class="Parent" column="parent_id" not-null="true"/>
- </class>
+ @OneToMany(mappedBy="parent")
+ private Set<Child> children;
-</hibernate-mapping>]]></programlisting>
+ // getter/setter
+ ...
+}
- <para>
- Notice the <literal>NOT NULL</literal> constraint:
- </para>
- <programlisting role="XML"><![CDATA[create table parent ( id bigint not null primary key )
+public class Child {
+ @Id
+ @GeneratedValue
+ private long id;
+
+ private String name;
+
+ @ManyToOne
+ private Parent parent;
+
+
+ // getter/setter
+ ...
+}</programlisting>
+ </example>
+
+ <example>
+ <title>One to many bidirectional <classname>Parent-Child</classname>
+ relationship using mapping files</title>
+
+ <programlisting role="XML"><hibernate-mapping>
+
+ <class name="Parent">
+ <id name="id">
+ <generator class="sequence"/>
+ </id>
+ <set name="children" inverse="true">
+ <key column="parent_id"/>
+ <one-to-many class="Child"/>
+ </set>
+ </class>
+
+ <class name="Child">
+ <id name="id">
+ <generator class="sequence"/>
+ </id>
+ <property name="name"/>
+ <many-to-one name="parent" class="Parent" column="parent_id" not-null="true"/>
+ </class>
+
+</hibernate-mapping></programlisting>
+ </example>
+
+ <para>Notice the <literal>NOT NULL</literal> constraint:</para>
+
+ <example>
+ <title>Table definitions for bidirectional
+ <classname>Parent</classname>-<classname>Child</classname>
+ relationship</title>
+
+ <programlisting role="XML">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>
+alter table child add constraint childfk0 (parent_id) references parent</programlisting>
+ </example>
- <para>
- Alternatively, if this association must be unidirectional
- you can declare the <literal>NOT NULL</literal> constraint on the <literal><key></literal>
- mapping:
- </para>
+ <para>Alternatively, if this association must be unidirectional you can
+ enforce the <literal>NOT NULL</literal> constraint.</para>
- <programlisting role="XML"><![CDATA[<hibernate-mapping>
+ <example>
+ <title>Enforcing NOT NULL constraint in unidirectional relation using
+ annotations</title>
- <class name="Parent">
- <id name="id">
- <generator class="sequence"/>
- </id>
- <set name="children">
- <key column="parent_id" not-null="true"/>
- <one-to-many class="Child"/>
- </set>
- </class>
+ <programlisting role="XML">public class Parent {
+ @Id
+ @GeneratedValue
+ private long id;
- <class name="Child">
- <id name="id">
- <generator class="sequence"/>
- </id>
- <property name="name"/>
- </class>
+ @OneToMany(optional=false)
+ private Set<Child> children;
-</hibernate-mapping>]]></programlisting>
+ // getter/setter
+ ...
+}
- <para>
- On the other hand, if a child has multiple parents, a many-to-many
- association is appropriate:
- </para>
- <programlisting role="XML"><![CDATA[<hibernate-mapping>
+public class Child {
+ @Id
+ @GeneratedValue
+ private long id;
+ private String name;
- <class name="Parent">
- <id name="id">
- <generator class="sequence"/>
- </id>
- <set name="children" table="childset">
- <key column="parent_id"/>
- <many-to-many class="Child" column="child_id"/>
- </set>
- </class>
+
+ // getter/setter
+ ...
+}</programlisting>
+ </example>
- <class name="Child">
- <id name="id">
- <generator class="sequence"/>
- </id>
- <property name="name"/>
- </class>
+ <example>
+ <title>Enforcing NOT NULL constraint in unidirectional relation using
+ mapping files</title>
-</hibernate-mapping>]]></programlisting>
+ <programlisting role="XML"><hibernate-mapping>
- <para>
- Table definitions:
- </para>
+ <class name="Parent">
+ <id name="id">
+ <generator class="sequence"/>
+ </id>
+ <set name="children">
+ <key column="parent_id" not-null="true"/>
+ <one-to-many class="Child"/>
+ </set>
+ </class>
- <programlisting><![CDATA[create table parent ( id bigint not null primary key )
+ <class name="Child">
+ <id name="id">
+ <generator class="sequence"/>
+ </id>
+ <property name="name"/>
+ </class>
+
+</hibernate-mapping></programlisting>
+ </example>
+
+ <para>On the other hand, if a child has multiple parents, a many-to-many
+ association is appropriate.</para>
+
+ <example>
+ <title>Many to many <classname>Parent-Child</classname> relationship
+ using annotations</title>
+
+ <programlisting role="XML">public class Parent {
+ @Id
+ @GeneratedValue
+ private long id;
+
+ @ManyToMany
+ private Set<Child> children;
+
+ // getter/setter
+ ...
+}
+
+
+public class Child {
+ @Id
+ @GeneratedValue
+ private long id;
+
+ private String name;
+
+
+ // getter/setter
+ ...
+}</programlisting>
+ </example>
+
+ <example>
+ <title>Many to many <classname>Parent-Child</classname> relationship
+ using mapping files</title>
+
+ <programlisting role="XML"><hibernate-mapping>
+
+ <class name="Parent">
+ <id name="id">
+ <generator class="sequence"/>
+ </id>
+ <set name="children" table="childset">
+ <key column="parent_id"/>
+ <many-to-many class="Child" column="child_id"/>
+ </set>
+ </class>
+
+ <class name="Child">
+ <id name="id">
+ <generator class="sequence"/>
+ </id>
+ <property name="name"/>
+ </class>
+
+</hibernate-mapping></programlisting>
+ </example>
+
+ <para>Table definitions:</para>
+
+ <example>
+ <title>Table definitions for many to many releationship</title>
+
+ <programlisting>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>
+alter table childset add constraint childsetfk1 (child_id) references child</programlisting>
+ </example>
- <para>
- For more examples and a complete explanation of a parent/child relationship mapping,
- see <xref linkend="example-parentchild"/> for more information.
- </para>
-
- <para>
- Even more complex association mappings are covered
- in the next chapter.
- </para>
-
- </section>
-
+ <para>For more examples and a complete explanation of a parent/child
+ relationship mapping, see <xref linkend="example-parentchild" /> for more
+ information. Even more complex association mappings are covered in the
+ next chapter.</para>
+ </section>
</chapter>
More information about the hibernate-commits
mailing list