Hibernate.orgCommunity Documentation
As a requirement persistent collection-valued fields must be
declared as an interface type (see Example 7.2, “Collection mapping using @OneToMany and @JoinColumn”). 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 in Example 7.2, “Collection mapping using @OneToMany and @JoinColumn” the instance variable
parts
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()
,
Hibernate will actually replace the HashSet
with an
instance of Hibernate's own implementation of Set
. Be
aware of the following 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.
Product describes a unidirectional relationship with Part using the join column PART_ID. In this unidirectional one to many scenario you can also use a join table as seen in Example 7.3, “Collection mapping using @OneToMany and @JoinTable”.
Without describing any physical mapping (no
@JoinColumn
or @JoinTable
),
a unidirectional one to many with join table is used. The table name is
the concatenation of the owner table name, _, and the other side table
name. The foreign key name(s) referencing the owner table is the
concatenation of the owner table, _, and the owner primary key column(s)
name. The foreign key name(s) referencing the other side is the
concatenation of the owner property name, _, and the other side primary
key column(s) name. A unique constraint is added to the foreign key
referencing the other side table to reflect the one to many.
Lets have a look now how collections are mapped using Hibernate
mapping files. In this case the first step is to chose the right mapping
element. It depends on the type of interface. For example, a
<set>
element is used for mapping properties of
type Set
.
Example 7.4. Mapping a Set using <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>
In Example 7.4, “Mapping a Set using <set>” a
one-to-many association links the
Product
and Part
entities. This
association requires the existence of a foreign key column and possibly an
index column to the Part
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.
Looking closer at the used <one-to-many>
tag we see that it has the following options.
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 Section 7.3.2, “Bidirectional associations”.
Apart from the <set>
tag as shown in Example 7.4, “Mapping a Set using <set>”, there is also
<list>
, <map>
,
<bag>
, <array>
and
<primitive-array>
mapping elements. The
<map>
element is representative:
After exploring the basic mapping of collections in the preceding paragraphs we will now focus details like physical mapping considerations, indexed collections and collections of value types.
@JoinColumn(nullable=false)
<key column="productSerialNumber" not-null="true"/>
The foreign key constraint can use ON DELETE
CASCADE
. In XML this can be expressed via:
<key column="productSerialNumber" on-delete="cascade"/>
In annotations the Hibernate specific annotation @OnDelete has to be used.
@OnDelete(action=OnDeleteAction.CASCADE)
See Section 5.1.11.3, “Key” for more information
about the <key>
element.
Lists can be mapped in two different ways:
To store the index value in a dedicated column, use the
@javax.persistence.OrderColumn
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
ORDER
(in the following example, it would be
orders_ORDER
).
Example 7.8. Explicit index column using
@OrderColumn
@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;
}
@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 |
|--------------|
We recommend you to convert the legacy
@org.hibernate.annotations.IndexColumn
usages to
@OrderColumn
unless you are making use of the
base property. The base
property lets you define
the index value of the first element (aka as base index). The usual
value is 0
or 1
. The default
is 0 like in Java.
Looking again at the Hibernate mapping file equivalent, 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.
Example 7.9. index-list element for indexed collections in xml mapping
<list-index column="column_name" base="
0|1|..."/>
| |
|
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.
Alternatively the map key is mapped to a dedicated column or columns. In order to customize the mapping use one of the following annotations:
@MapKeyColumn
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 KEY
is used (for example orders_KEY
).
@MapKeyEnumerated
/
@MapKeyTemporal
if the map key type is
respectively an enum or a Date
.
@MapKeyJoinColumn
/@MapKeyJoinColumns
if the map key type is another entity.
@AttributeOverride
/@AttributeOverrides
when the map key is a embeddable object. Use
key.
as a prefix for your embeddable object
property names.
You can also use @MapKeyClass
to define
the type of the key if you don't use generics.
Example 7.11. Map key as basic type using
@MapKeyColumn
@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;
}
@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 |
|-------------| |---------------|
We recommend you to migrate from
@org.hibernate.annotations.MapKey
/
@org.hibernate.annotation.MapKeyManyToMany
to
the new standard approach described above
Using Hibernate mapping files there exists equivalent concepts
to the descibed annotations. You have to use
<map-key>
,
<map-key-many-to-many>
and
<composite-map-key>
.
<map-key>
is used for any basic type,
<map-key-many-to-many>
for an entity
reference and <composite-map-key>
for a
composite type.
Example 7.12. map-key xml mapping element
<map-key column="column_name" formul
a="any SQL expression" type="
type_name" node="@attribute-name" length="N"/>
| |
| |
|
Example 7.13. map-key-many-to-many
<map-key-many-to-many column="column_name" formul
a="any SQL expression" class="ClassName" />
| |
| |
|
The collection table holding the collection data is set using the
@CollectionTable
annotation. If omitted the
collection table name defaults 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
User_nicknames
.
The column holding the basic type is set using the
@Column
annotation. If omitted, the column name
defaults to the property name: in our example, it would be
nicknames
.
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
@AttributeOverride
annotation.
Example 7.15. @ElementCollection for embeddable objects
@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() { ... }
}
@Embeddable
public class Address {
public String getStreet1() {...}
[...]
}
Such an embeddable object cannot contains a collection itself.
in @AttributeOverride
, you must use the
value.
prefix to override properties of the
embeddable object used in the map value and the
key.
prefix to override properties of the
embeddable object used in the map key.
@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() { ... }
We recommend you to migrate from
@org.hibernate.annotations.CollectionOfElements
to the new @ElementCollection
annotation.
Using the mapping file approach a collection of values is mapped
using the <element>
tag. For example:
Example 7.16. <element> tag for collection values using mapping files
<element column="column_name" formul
a="any SQL expression" type="
typename" length="L" precision="P" scale="S" not-null="true|false" unique="true|false" node="element-name" />
| |
| |
|
Using Hibernate mapping files you specify a comparator in the
mapping file with <sort>
:
Example 7.18. Sorted collection using xml mapping
<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 implemented using LinkedHashSet
or
LinkedHashMap
and performs the ordering in the SQL
query and not in the memory.
Example 7.19. Sorting in database using order-by
<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()
:
Example 7.20. Sorting via a query filter
sortedUsers = s.createFilter( group.getUsers(), "order by this.name" ).list();
Troop
has a bidirectional one to many
relationship with Soldier
through the
troop
property. You don't have to (must not) define
any physical mapping in the mappedBy
side.
To map a bidirectional one to many, with the one-to-many side as
the owning side, you have to remove the mappedBy
element and set the many to one @JoinColumn
as
insertable and updatable to false. This solution is not optimized and
will produce additional UPDATE statements.
Example 7.22. Bidirectional associtaion with one to many side as owner
@Entity
public class Troop {
@OneToMany
@JoinColumn(name="troop_fk") //we need to duplicate the physical information
public Set<Soldier> getSoldiers() {
...
}
@Entity
public class Soldier {
@ManyToOne
@JoinColumn(name="troop_fk", insertable=false, updatable=false)
public Troop getTroop() {
...
}
How does the mappping of a bidirectional mapping look like in
Hibernate mapping xml? There you 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"
.
Example 7.23. Bidirectional one to many via Hibernate mapping files
<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 many-to-many association is defined logically using the
@ManyToMany
annotation. You also have to describe the
association table and the join conditions using the
@JoinTable
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):
Example 7.24. Many to many association via @ManyToMany
@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;
}
...
}
@Entity
public class Employee implements Serializable {
@ManyToMany(
cascade = {CascadeType.PERSIST, CascadeType.MERGE},
mappedBy = "employees",
targetEntity = Employer.class
)
public Collection getEmployers() {
return employers;
}
}
In this example @JoinTable
defines a
name
, 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 Employee
primary key
(the "other side"). As seen previously, the other side don't have to
(must not) describe the physical mapping: a simple
mappedBy
argument containing the owner side property
name bind the two.
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, _ and the other side table name. The foreign key name(s) referencing the owner table is the concatenation of the owner table name, _ and the owner primary key column(s). The foreign key name(s) referencing the other side is the concatenation of the owner property name, _, and the other side primary key column(s). These are the same rules used for a unidirectional one to many relationship.
Example 7.25. Default values for @ManyToMany
(uni-directional)
@Entity
public class Store {
@ManyToMany(cascade = CascadeType.PERSIST)
public Set<City> getImplantedIn() {
...
}
}
@Entity
public class City {
... //no bidirectional relationship
}
A Store_City
is used as the join table. The
Store_id
column is a foreign key to the
Store
table. The implantedIn_id
column is a foreign key to the City
table.
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, _ and the other side table name. The foreign key name(s) referencing the owner table is the concatenation of the other side property name, _, and the owner primary key column(s). The foreign key name(s) referencing the other side is the concatenation of the owner property name, _, and the other side primary key column(s). These are the same rules used for a unidirectional one to many relationship.
Example 7.26. Default values for @ManyToMany
(bi-directional)
@Entity
public class Store {
@ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
public Set<Customer> getCustomers() {
...
}
}
@Entity
public class Customer {
@ManyToMany(mappedBy="customers")
public Set<Store> getStores() {
...
}
}
A Store_Customer
is used as the join table. The
stores_id
column is a foreign key to the
Store
table. The customers_id
column is a foreign key to the Customer
table.
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 inverse.
You cannot select an indexed collection.
Example 7.27, “Many to many association using Hibernate mapping files” shows a bidirectional many-to-many association that illustrates how each category can have many items and each item can be in many categories:
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:
Example 7.28. Effect of inverse vs. non-inverse side of many to many associations
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.
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:
Example 7.30. Bidirectional association with indexed collection, but no index column
<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.
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>
This section covers collection examples.
The following class has a collection of Child
instances:
If each child has, at most, one parent, the most natural mapping is a one-to-many association:
Example 7.33. One to many unidirectional Parent-Child
relationship using annotations
public class Parent {
@Id
@GeneratedValue
private long id;
@OneToMany
private Set<Child> children;
// getter/setter
...
}
public class Child {
@Id
@GeneratedValue
private long id;
private String name;
// getter/setter
...
}
Example 7.34. One to many unidirectional Parent-Child
relationship using mapping files
<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:
Example 7.35. Table definitions for unidirectional
Parent
-Child
relationship
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:
Example 7.36. One to many bidirectional Parent-Child
relationship using annotations
public class Parent {
@Id
@GeneratedValue
private long id;
@OneToMany(mappedBy="parent")
private Set<Child> children;
// getter/setter
...
}
public class Child {
@Id
@GeneratedValue
private long id;
private String name;
@ManyToOne
private Parent parent;
// getter/setter
...
}
Example 7.37. One to many bidirectional Parent-Child
relationship using mapping files
<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:
Example 7.38. Table definitions for bidirectional
Parent
-Child
relationship
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
enforce the NOT NULL
constraint.
Example 7.39. Enforcing NOT NULL constraint in unidirectional relation using annotations
public class Parent {
@Id
@GeneratedValue
private long id;
@OneToMany(optional=false)
private Set<Child> children;
// getter/setter
...
}
public class Child {
@Id
@GeneratedValue
private long id;
private String name;
// getter/setter
...
}
Example 7.40. Enforcing NOT NULL constraint in unidirectional relation using mapping files
<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.
Example 7.41. Many to many Parent-Child
relationship
using annotations
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
...
}
Example 7.42. Many to many Parent-Child
relationship
using mapping files
<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:
Example 7.43. Table definitions for many to many releationship
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 24, Example: Parent/Child for more information. Even more complex association mappings are covered in the next chapter.
Copyright © 2004 Red Hat, Inc.