Hibernate.orgCommunity Documentation
Naturally 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. The distinction between value and reference semantics is in this context very important. 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.
As a requirement persistent collection-valued fields must be declared as an interface type (see Exemplo 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 Exemplo 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:
Exemplo 7.1. Hibernate uses its own collection implementations
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!
As coleções persistentes injetadas pelo Hibernate, se comportam como HashMap
, HashSet
, TreeMap
, TreeSet
ou ArrayList
, dependendo do tipo de interface.
As instâncias de coleção têm o comportamento comum de tipos de valores. Eles são automaticamente persistidos quando referenciados por um objeto persistente e automaticamente deletados quando não referenciados. Se a coleção é passada de um objeto persistente para outro, seus elementos devem ser movidos de uma tabela para outra. Duas entidades não devem compartilhar uma referência com uma mesma instância de coleção. Devido ao modelo relacional adjacente, as propriedades de coleções válidas, não suportam semânticas de valores nulos. O Hibernate não distingue entre a referência da coleção nula e uma coleção vazia.
Use persistent collections the same way you use ordinary Java collections. However, ensure you understand the semantics of bidirectional associations (see Seção 7.3.2, “Associações Bidirecionais”).
Using annotations you can map Collection
s, List
s, Map
s and Set
s 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:
Exemplo 7.2. Collection mapping using @OneToMany and @JoinColumn
@Entity
public class Product {
private String serialNumber;
private Set<Part> parts = new HashSet<Part>();
@Id
public String getSerialNumber() { return serialNumber; }
void setSerialNumber(String sn) { serialNumber = sn; }
@OneToMany
@JoinColumn(name="PART_ID")
public Set<Part> getParts() { return parts; }
void setParts(Set parts) { this.parts = parts; }
}
@Entity
public class Part {
...
}
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 Exemplo 7.3, “Collection mapping using @OneToMany and @JoinTable”.
Exemplo 7.3. Collection mapping using @OneToMany and @JoinTable
@Entity
public class Product {
private String serialNumber;
private Set<Part> parts = new HashSet<Part>();
@Id
public String getSerialNumber() { return serialNumber; }
void setSerialNumber(String sn) { serialNumber = sn; }
@OneToMany
@JoinTable(
name="PRODUCT_PARTS",
joinColumns = @JoinColumn( name="PRODUCT_ID"),
inverseJoinColumns = @JoinColumn( name="PART_ID")
)
public Set<Part> getParts() { return parts; }
void setParts(Set parts) { this.parts = parts; }
}
@Entity
public class Part {
...
}
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
.
Exemplo 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 Exemplo 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:
Uma instância de classes entidades contidas, podem não pertencer à mais de uma instância da coleção.
Uma instância da classe de entidade contida pode não aparecer em mais de um valor do índice da coleção.
Looking closer at the used <one-to-many>
tag we see that it has the following options.
Exemplo 7.5. options of <one-to-many> element
<one-to-many class="ClassName" not-fo
und="ignore|exception" entity
-name="EntityName" node="element-name" embed-xml="true|false" />
| |
| |
|
Note que o elemento <one-to-many>
não precisa declarar qualquer coluna. Nem é necessário especificar o nome da table
em qualquer lugar.
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 Seção 7.3.2, “Associações Bidirecionais”.
Apart from the <set>
tag as shown in Exemplo 7.4, “Mapping a Set using <set>”, there is also <list>
, <map>
, <bag>
, <array>
and <primitive-array>
mapping elements. The <map>
element is representative:
Exemplo 7.6. Elements of the <map> mapping
<map name="propertyName" table="tab
le_name" schema="sc
hema_name" lazy="true
|extra|false" inverse="t
rue|false" cascade="a
ll|none|save-update|delete|all-delete-orphan|delete-orphan" sort="unso
rted|natural|comparatorClass" order-by="
column_name asc|desc" where="arb
itrary sql where condition" fetch="joi
n|select|subselect" batch-size
="N" access="fi
eld|property|ClassName" optimistic
-lock="true|false" mutable="t
rue|false" node="element-name|." embed-xml="true|false" > <key .... /> <map-key .... /> <element .... /> </map>
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
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.
On the database level collection instances are distinguished 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 @JoinColumn
annotation respectively the <key>
XML 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
@JoinColumn(nullable=false)
or
<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 Seção 5.1.11.3, “Key” for more information about the <key>
element.
In the following paragraphs we have a closer at the indexed collections List
and Map
how the their index can be mapped in Hibernate.
Lists can be mapped in two different ways:
as ordered lists, where the order is not materialized in the database
as indexed lists, where the order is materialized in the database
To order lists in memory, add @javax.persistence.OrderBy
to your property. This annotation takes as parameter a list of comma separated properties (of the target entity) and orders the collection accordingly (eg firstname asc, age desc
), if the string is empty, the collection will be ordered by the primary key of the target entity.
Exemplo 7.7. Ordered lists using @OrderBy
@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;
}
@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 |
|-------------|
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
).
Exemplo 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.
Exemplo 7.9. index-list element for indexed collections in xml mapping
<list-index column="column_name" base="
0|1|..."/>
| |
|
Se sua tabela não possui uma coluna de índice e você ainda quiser usar a Lista
como tipo de propriedade, você deve mapeiar a propriedade como uma <bag> do Hibernate. Uma bag não mantém sua ordem quando é recuperadada do banco de dados, mas pode ser escolhida de forma opcional ou ordenada.
The question with Map
s is where the key value is stored. There are everal options. Maps can borrow their keys from one of the associated entity properties or have dedicated columns to store an explicit key.
To use one of the target entity property as a key of the map, use @MapKey(name="myProperty")
, where myProperty
is a property name in the target entity. When using @MapKey
without the name attribuate, 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, because the map key 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.
Exemplo 7.10. Use of target entity property as map key via @MapKey
@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;
}
@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 |
|-------------|
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.
Exemplo 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.
Exemplo 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"/>
| |
| |
|
Exemplo 7.13. map-key-many-to-many
<map-key-many-to-many column="column_name" formul
a="any SQL expression" class="ClassName" />
| |
| |
|
In some situations you don't need to associate two entities but simply create a collection of basic types or embeddable objects. Use the @ElementCollection
for this case.
Exemplo 7.14. Collection of basic types mapped via @ElementCollection
@Entity
public class User {
[...]
public String getLastname() { ...}
@ElementCollection
@CollectionTable(name="Nicknames", joinColumns=@JoinColumn(name="user_id"))
@Column(name="nickname")
public Set<String> getNicknames() { ... }
}
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.
Exemplo 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:
Exemplo 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" />
| |
| |
|
Hibernate supports collections implementing java.util.SortedMap
and java.util.SortedSet
. With annotations you declare a sort comparator using @Sort
. You chose between the comparator types unsorted, natural or custom. If you want to use your own comparator implementation, you'll also have to specify the implementation class using the comparator
attribute. Note that you need to use either a SortedSet
or a SortedMap
interface.
Exemplo 7.17. Sorted collection with @Sort
@OneToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER)
@JoinColumn(name="CUST_ID")
@Sort(type = SortType.COMPARATOR, comparator = TicketComparator.class)
public SortedSet<Ticket> getTickets() {
return tickets;
}
Using Hibernate mapping files you specify a comparator in the mapping file with <sort>
:
Exemplo 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>
Valores permitidos da funçãosort
sãounsorted
, natural
e o nome de uma classe implementando java.util.Comparator
.
Coleções escolhidas, na verdade se comportam como java.util.TreeSet
ou 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.
Exemplo 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>
Note que o valor da função order-by
é uma ordenação SQL e não uma ordenação.
Associações podem também ser escolhidas por algum critério arbritrário em tempo de espera usando uma coleção filter()
:
Exemplo 7.20. Sorting via a query filter
sortedUsers = s.createFilter( group.getUsers(), "order by this.name" ).list();
Uma associação bidirecional permite a navegação de ambos os "lados" da associação. Dois dos casos de associação bidirecional, são suportados:
conjunto ou bag de valor em um dos lados, valor único do outro
Conjunto ou bag com valor em ambos os lados
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 @OneToMany(mappedBy=...)
Exemplo 7.21. Bidirectional one to many with many to one side as association owner
@Entity
public class Troop {
@OneToMany(mappedBy="troop")
public Set<Soldier> getSoldiers() {
...
}
@Entity
public class Soldier {
@ManyToOne
@JoinColumn(name="troop_fk")
public Troop getTroop() {
...
}
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.
Exemplo 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"
.
Exemplo 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>
Mapear apenas uma das pontas da associação com inverse="true"
não afeta as operações em cascata, uma vez que isto é um conceito ortogonal.
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):
Exemplo 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.
Exemplo 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.
Exemplo 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.
Exemplo 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:
Exemplo 7.27. Many to many association using Hibernate mapping files
<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>
As mudanças feitas somente de um lado da associação não são persistidas. Isto significa que o Hibernate tem duas representações na memória para cada associação bidirecional, uma associação de A para B e uma outra associação de B para A. Isto é mais fácil de compreender se você pensa sobre o modelo de objetos do Java e como criamos um relacionamento muitos para muitos em Java:
Exemplo 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
A outra ponta é usada para salvar a representação em memória à base de dados.
There are some additional considerations for bidirectional mappings with indexed collections (where one end is represented as a <list>
or <map>
) when using Hibernate mapping files. If there is a property of the child class that maps to the index column you can use inverse="true"
on the collection mapping:
Exemplo 7.29. Bidirectional association with indexed collection
<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>
Mas, se não houver nenhuma propriedade na classe filha, não podemos ver essa associação como verdadeiramente bidirecional (há uma informação disponível em um lado da associação que não está disponível no extremo oposto). Nesse caso, nós não podemos mapear a coleção usando inverse="true"
. Devemos usar o seguinte mapeamento:
Exemplo 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>
Veja que neste mapeamento, o lado de coleção válida da associação é responsável pela atualização da chave exterior.
Há três meios possíveis de se mapear uma associação ternária. Uma é usar um Map
com uma associação como seu índice:
Exemplo 7.31. Ternary association mapping
@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>
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 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. 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.
O elemento <idbag>
permite mapear um List
(ou uma Collection
) com uma semântica de bag. Por exemplo:
<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>
O <idbag>
possui um gerador de id sintético, igual a uma classe de entidade. Uma chave substituta diferente é associada para cada elemento de coleção. Porém, o Hibernate não provê de nenhum mecanismo para descobrir qual a chave substituta de uma linha em particular.
Note que o desempenho de atualização de um <idbag>
é melhor do que um <bag>
normal. O Hibernate pode localizar uma linha individual eficazmente e atualizar ou deletar individualmente, como um list, map ou set.
Na implementação atual, a estratégia de geração de identificador native
não é suportada para identificadores de coleção usando o <idbag>
.
Esta sessão cobre os exemplos de coleções.
A seguinte classe possui uma coleção de instâncias Child
:
Exemplo 7.32. Example classes Parent
and Child
public class Parent {
private long id;
private Set<Child> children;
// getter/setter
...
}
public class Child {
private long id;
private String name
// getter/setter
...
}
Se cada Filho tiver no máximo um Pai, o mapeamento natural é uma associação um para muitos:
Exemplo 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
...
}
Exemplo 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>
Esse mapeamento gera a seguinte definição de tabelas
Exemplo 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
Se o pai for obrigatório, use uma associação bidirecional um para muitos:
Exemplo 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
...
}
Exemplo 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>
Repare na restrição NOT NULL
:
Exemplo 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.
Exemplo 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
...
}
Exemplo 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.
Exemplo 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
...
}
Exemplo 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>
Definições das tabelas:
Exemplo 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 Capítulo 24, Exemplo: Pai/Filho for more information. Even more complex association mappings are covered in the next chapter.
Copyright © 2004 Red Hat, Inc.