Hibernate.orgCommunity Documentation
Hibernate requires that persistent collection-valued fields be declared as an interface type. For example:
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; } }
The actual interface might be java.util.Set
,
java.util.Collection
, java.util.List
,
java.util.Map
, java.util.SortedSet
,
java.util.SortedMap
or anything you like
("anything you like" means you will have to write an implementation of
org.hibernate.usertype.UserCollectionType
.)
Notice how the instance variable was initialized with an instance of
HashSet
. This is the best way to initialize collection
valued properties of newly instantiated (non-persistent) instances. When
you make the instance persistent, by calling persist()
for example, Hibernate will actually replace the HashSet
with an instance of Hibernate's own implementation of Set
.
Be aware of the following errors:
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!
The persistent collections injected by Hibernate behave like
HashMap
, HashSet
,
TreeMap
, TreeSet
or
ArrayList
, depending on the interface type.
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.
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).
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.
The Hibernate mapping element used for mapping a collection depends upon
the type of interface. For example, a <set>
element is used for mapping properties of type Set
.
<class name="Product"> <id name="serialNumber" column="productSerialNumber"/> <set name="parts"> <key column="productSerialNumber" not-null="true"/> <one-to-many class="Part"/> </set> </class>
Apart from <set>
, there is also
<list>
, <map>
,
<bag>
, <array>
and
<primitive-array>
mapping elements. The
<map>
element is representative:
<map name="propertyName" table="table_name" schema="schema_name" lazy="true|extra|false" inverse="true|false" cascade="all|none|save-update|delete|all-delete-orphan|delete-orphan" sort="unsorted|natural|comparatorClass" order-by="column_name asc|desc" where="arbitrary sql where condition" fetch="join|select|subselect" batch-size="N" access="field|property|ClassName" optimistic-lock="true|false" mutable="true|false" node="element-name|." embed-xml="true|false" > <key .... /> <map-key .... /> <element .... /> </map>
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
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
collection key column, or columns, of the collection
table. The collection key column is mapped by the <key>
element.
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
not-null="true"
.
<key column="productSerialNumber" not-null="true"/>
The foreign key constraint can use ON DELETE CASCADE
.
<key column="productSerialNumber" on-delete="cascade"/>
See the previous chapter for a full definition of the <key>
element.
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.
The contained type is referred to as the collection element type.
Collection elements are mapped by <element>
or
<composite-element>
, or in the case of entity references,
with <one-to-many>
or <many-to-many>
.
The first two map elements with value semantics, the next two are used to map entity
associations.
All collection mappings, except those with set and bag semantics, need an
index column in the collection table. An index column is a column that maps to an
array index, or List
index, or Map
key. The
index of a Map
may be of any basic type, mapped with
<map-key>
. It can be an entity reference mapped with
<map-key-many-to-many>
, or it can be a composite type
mapped with <composite-map-key>
. The index of an array or
list is always of type integer
and is mapped using the
<list-index>
element. The mapped column contains
sequential integers that are numbered from zero by default.
<list-index column="column_name" base="0|1|..."/>
| |
|
<map-key column="column_name" formula="any SQL expression" type="type_name" node="@attribute-name" length="N"/>
| |
| |
|
<map-key-many-to-many column="column_name" formula="any SQL expression" class="ClassName" />
| |
| |
|
If your table does not have an index column, and you still wish to use List
as the property type, you can map the property as a Hibernate <bag>.
A bag does not retain its order when it is retrieved from the database, but it can be
optionally sorted or ordered.
Any collection of values or many-to-many associations requires a dedicated collection table with a foreign key column or columns, collection element column or columns, and possibly an index column or columns.
For a collection of values use the <element>
tag. For example:
<element column="column_name" formula="any SQL expression" type="typename" length="L" precision="P" scale="S" not-null="true|false" unique="true|false" node="element-name" />
| |
| |
|
A many-to-many association is specified using the
<many-to-many>
element.
<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" />
| |
| |
| |
| |
| |
| |
| |
|
Here are some examples.
A set of strings:
<set name="names" table="person_names"> <key column="person_id"/> <element column="person_name" type="string"/> </set>
A bag containing integers with an iteration order determined by the
order-by
attribute:
<bag name="sizes" table="item_sizes" order-by="size asc"> <key column="item_id"/> <element column="size" type="integer"/> </bag>
An array of entities, in this case, a many-to-many association:
<array name="addresses" table="PersonAddress" cascade="persist"> <key column="personId"/> <list-index column="sortOrder"/> <many-to-many column="addressId" class="Address"/> </array>
A map from string indices to dates:
<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>
A list of components (this is discussed in the next chapter):
<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>
A one-to-many association links the tables of two classes via a foreign key with no intervening collection table. This mapping loses certain semantics of normal Java collections:
An instance of the contained entity class cannot belong to more than one instance of the collection.
An instance of the contained entity class cannot appear at more than one value of the collection index.
An association from Product
to Part
requires
the existence of a foreign key column and possibly an index column to the Part
table. A <one-to-many>
tag indicates that this is a one-to-many
association.
<one-to-many class="ClassName" not-found="ignore|exception" entity-name="EntityName" node="element-name" embed-xml="true|false" />
| |
| |
|
The <one-to-many>
element does not need to
declare any columns. Nor is it necessary to specify the table
name anywhere.
If the foreign key column of a
<one-to-many>
association is declared NOT NULL
,
you must declare the <key>
mapping
not-null="true"
or use a bidirectional association
with the collection mapping marked inverse="true"
. See the discussion
of bidirectional associations later in this chapter for more information.
The following example shows a map of Part
entities by name, where
partName
is a persistent property of Part
.
Notice the use of a formula-based index:
<map name="parts" cascade="all"> <key column="productId" not-null="true"/> <map-key formula="partName"/> <one-to-many class="Part"/> </map>
Hibernate supports collections implementing java.util.SortedMap
and
java.util.SortedSet
. You must specify a comparator in the mapping file:
<set name="aliases" table="person_aliases" sort="natural"> <key column="person"/> <element column="name" type="string"/> </set> <map name="holidays" sort="my.custom.HolidayComparator"> <key column="year_id"/> <map-key column="hol_name" type="string"/> <element column="hol_date" type="date"/> </map>
Allowed values of the sort
attribute are unsorted
,
natural
and the name of a class implementing
java.util.Comparator
.
Sorted collections actually behave like java.util.TreeSet
or
java.util.TreeMap
.
If you want the database itself to order the collection elements, use the
order-by
attribute of set
, bag
or map
mappings. This solution is only available under
JDK 1.4 or higher and is implemented using LinkedHashSet
or
LinkedHashMap
. This performs the ordering in the SQL query and
not in the memory.
<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>
The value of the order-by
attribute is an SQL ordering, not
an HQL ordering.
Associations can even be sorted by arbitrary criteria at runtime using a collection
filter()
:
sortedUsers = s.createFilter( group.getUsers(), "order by this.name" ).list();
A bidirectional association allows navigation from both "ends" of the association. Two kinds of bidirectional association are supported:
set or bag valued at one end and single-valued at the other
set or bag valued at both ends
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 inverse. You cannot select an indexed collection.
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:
<class name="Category"> <id name="id" column="CATEGORY_ID"/> ... <bag name="items" table="CATEGORY_ITEM"> <key column="CATEGORY_ID"/> <many-to-many class="Item" column="ITEM_ID"/> </bag> </class> <class name="Item"> <id name="id" column="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>
Changes made only to the inverse end of the association are not 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:
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
The non-inverse side is used to save the in-memory representation to the database.
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 inverse="true"
.
<class name="Parent"> <id name="id" column="parent_id"/> .... <set name="children" inverse="true"> <key column="parent_id"/> <one-to-many class="Child"/> </set> </class> <class name="Child"> <id name="id" column="child_id"/> .... <many-to-one name="parent" class="Parent" column="parent_id" not-null="true"/> </class>
Mapping one end of an association with inverse="true"
does not
affect the operation of cascades as these are orthogonal concepts.
A bidirectional association where one end is represented as a <list>
or <map>
, requires special consideration. If there is a property of
the child class that maps to the index column you can use
inverse="true"
on the collection mapping:
<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> <class name="Child"> <id name="id" column="child_id"/> .... <property name="name" not-null="true"/> <many-to-one name="parent" class="Parent" column="parent_id" not-null="true"/> </class>
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
inverse="true"
. Instead, you could use the following mapping:
<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> <class name="Child"> <id name="id" column="child_id"/> .... <many-to-one name="parent" class="Parent" column="parent_id" insert="false" update="false" not-null="true"/> </class>
Note that in this mapping, the collection-valued end of the association is responsible for updates to the foreign key.
There are three possible approaches to mapping a ternary association. One approach is to use a
Map
with an association as its index:
<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>
<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>
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.
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 might. 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.
The <idbag>
element lets you map a List
(or Collection
) with bag semantics. For example:
<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>
An <idbag>
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.
The update performance of an <idbag>
supersedes a regular <bag>
.
Hibernate can locate individual rows efficiently and update or delete them
individually, similar to a list, map or set.
In the current implementation, the native
identifier generation
strategy is not supported for <idbag>
collection identifiers.
This section covers collection examples.
The following class has a collection of Child
instances:
package eg; import java.util.Set; public class Parent { private long id; private Set children; public long getId() { return id; } private void setId(long id) { this.id=id; } private Set getChildren() { return children; } private void setChildren(Set children) { this.children=children; } .... .... }
If each child has, at most, one parent, the most natural mapping is a one-to-many association:
<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>
This maps to the following table definitions:
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
If the parent is required, use a bidirectional one-to-many association:
<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>
Notice the NOT NULL
constraint:
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
Alternatively, if this association must be unidirectional
you can declare the NOT NULL
constraint on the <key>
mapping:
<hibernate-mapping> <class name="Parent"> <id name="id"> <generator class="sequence"/> </id> <set name="children"> <key column="parent_id" not-null="true"/> <one-to-many class="Child"/> </set> </class> <class name="Child"> <id name="id"> <generator class="sequence"/> </id> <property name="name"/> </class> </hibernate-mapping>
On the other hand, if a child has multiple parents, a many-to-many association is appropriate:
<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>
Table definitions:
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
For more examples and a complete explanation of a parent/child relationship mapping, see Chapter 21, Example: Parent/Child for more information.
Even more complex association mappings are covered in the next chapter.
Copyright © 2004 Red Hat Middleware, LLC.