Hibernate.orgCommunity Documentation

第7章 コレクションのマッピング

7.1. コレクションの永続化
7.2. How to map collections
7.2.1. コレクションの外部キー
7.2.2. インデックス付きのコレクション
7.2.3. Collections of basic types and embeddable objects
7.3. 高度なコレクションマッピング
7.3.1. ソートされたコレクション
7.3.2. 双方向関連
7.3.3. インデックス付きコレクションと双方向関連
7.3.4. 3項関連
7.3.5. Using an <idbag>
7.4. コレクションの例

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 例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 例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:


Hibernate により注入された永続性コレクションは、インターフェース型に応じて、 HashMapHashSetTreeMapTreeSetArrayList のように振舞います。

コレクションインスタンスは、値型として普通に振舞います。永続化オブジェクトに参照されたときに自動的に永続化され、参照がなくなったときに自動的に削除されます。もしある永続化オブジェクトから別の永続化オブジェクトに渡されたら、その要素は現在のテーブルから別のテーブルに移動するかもしれません。2つのエンティティが同じコレクションインスタンスを共有してはいけません。リレーショナルモデルをベースにしているため、コレクション型のプロパティに null 値を代入しても意味がありません。つまり Hibernate は参照先のないコレクションと空のコレクションを区別しません。

Using annotations you can map Collections, Lists, Maps and Sets 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:


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 例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.


In 例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:

  • エンティティクラスのインスタンスは、2つ以上のコレクションのインスタンスに属してはいけません。

  • コレクションに含まれるエンティティクラスのインスタンスは、コレクションインデックスの値として2度以上現れてはいけません。

Looking closer at the used <one-to-many> tag we see that it has the following options.


<one-to-many> 要素はカラムを宣言する必要がないことに注意してください。同様に テーブル 名を指定する必要もありません。

Apart from the <set> tag as shown in 例7.4「Mapping a Set using <set>」, there is also <list>, <map>, <bag>, <array> and <primitive-array> mapping elements. The <map> element is representative:

例7.6 Elements of the <map> mapping

<map
    name="prop(1)ertyName"
    table="tab(2)le_name"
    schema="sc(3)hema_name"
    lazy="true(4)|extra|false"
    inverse="t(5)rue|false"
    cascade="a(6)ll|none|save-update|delete|all-delete-orphan|delete-orphan"
    sort="unso(7)rted|natural|comparatorClass"
    order-by="(8)column_name asc|desc"
    where="arb(9)itrary sql where condition"
    fetch="joi(10)n|select|subselect"
    batch-size(11)="N"
    access="fi(12)eld|property|ClassName"
    optimistic(13)-lock="true|false"
    mutable="t(14)rue|false"
    node="element-name|."
    embed-xml="true|false"
>

    <key .... />
    <map-key .... />
    <element .... />
</map>

1

name :コレクション型であるプロパティの名前

2

table (オプション - デフォルトはプロパティ名):コレクションテーブルの名前(一対多関連では使用しません)。

3

schema (オプション):テーブルスキーマの名前。ルート要素で宣言されているスキーマより優先されます。

4

lazy (オプション - デフォルトは true): 遅延フェッチを無効にし、関連を常に即時にフェッチにするために使用します。または、「extra-lazy」フェッチを有効にするために使用します。「extra-lazy」フェッチは、ほとんどの操作ではコレクションを初期化しません (非常に大きなコレクションに適しています)。

5

inverse (オプション - デフォルトは false):このコレクションが双方向関連の「逆」側であるとマークします。

6

cascade (オプション - デフォルトは none):子エンティティへのカスケード操作を有効にします。

7

sort (オプション):コレクションを自然な順序でソートする場合は natural を指定します。あるいは Comparator クラスを指定します。

8

order-by (optional): specifies a table column or columns that define the iteration order of the Map, Set or bag, together with an optional asc or desc.

9

where (オプション):コレクションの検索や削除の際に使う任意の SQL のWHERE 条件を指定します (利用可能なデータの一部分だけをコレクションが含むべきときに、これは有用です)。

10

fetch (オプション - デフォルトは select):外部結合によるフェッチ、順次選択フェッチ (sequential select fetch) 、順次サブセレクトフェッチ (sequential subselect fetch) のどれかを選択してください。

11

batch-size (オプション - デフォルトは 1):コレクションのインスタンスの遅延フェッチのための「バッチサイズ」を指定します。

12

access (オプション - デフォルトは property):コレクション型プロパティの値にアクセスするために使用する戦略です。

13

optimistic-lock (オプション - デフォルトは true) コレクションの状態を変えることによって、そのオーナーであるエンティティのバージョンがインクリメントされるかを指定します。 (一対多関連では、ほとんどの場合において無効に設定するのが妥当です。)

14

mutable(オプション - デフォルトは truefalse 値は、コレクションの要素が変更されないことを表します (ある場合には、少しパフォーマンスを高めます)。


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.

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:

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.


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).


注意

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.


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.

The question with Maps 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.


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.

例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.



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.


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.


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:


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.


Using Hibernate mapping files you specify a comparator in the mapping file with <sort>:


sort 属性に設定できる値は unsortednatural および、 java.util.Comparator を実装したクラスの名前です。

ティップ

ソートされたコレクションは実質的には java.util.TreeSetjava.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.


注記

The value of the order-by attribute is an SQL ordering, not an HQL ordering.

関連は、コレクションの filter() を使うことで、実行時に任意の criteria によってソートすることも可能です。


双方向関連 は関連のどちら「側」からでもナビゲーションできます。2種類の双方向関連がサポートされています:

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=...)


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.


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".


関連の片側に inverse="true" を設定しても、カスケード操作に影響を与えません。これらは直交した概念です。

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):


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.


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.


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.

例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:


関連の inverse 側にのみ行われた変更は永続化 されません。これは、 Hibernate は全ての双方向関連について、メモリ上に2つの表現を持っているという意味です。つまり一つは A から B へのリンクで、もう一つは B から A へのリンクということです。 Java のオブジェクトモデルについて考え、 Java で双方向関係をどうやって作るかを考えれば、これは理解しやすいです。下記に、 Java での双方向関連を示します。


関連の inverse ではない側は、メモリ上の表現をデータベースに保存するのに使われます。

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:


しかし、子クラスにそのようなプロパティがない場合は、関連を真に双方向であると考えることができません (関連の片側に利用できる情報がありますが、もう一方にはありません)。この場合は、コレクションに inverse="true" をマッピングできません。代わりに、次のようなマッピングが使えます:


Note that in this mapping, the collection-valued end of the association is responsible for updates to the foreign key.

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.

bag のセマンティックスを持った List(または Collection)を <idbag> 要素にマッピングできます。


<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>

ご存知のように <idbag> はエンティティクラスのように人工的な id ジェネレータを持っています。異なる代理キーをそれぞれのコレクションの列に割り当てます。しかし、 Hibernate はある行の代理キーの値を見つけ出す機構を持っていません。

<idbag> を更新するパフォーマンスは通常の <bag> よりも良いことに注目してください。 Hibernate は個々の行を効果的に見つけることができ、 list や map 、 set のように個別にその行を更新、削除できます。

現在の実装では、 native という id 生成戦略を <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:



これは以下のテーブル定義にマッピングします。


もし parent が 要求 されるなら、双方向の一対多関連を使用してください:



NOT NULL 制約に注意してください。


Alternatively, if this association must be unidirectional you can enforce the NOT NULL constraint.



On the other hand, if a child has multiple parents, a many-to-many association is appropriate.



テーブル定義は以下のようになります:


For more examples and a complete explanation of a parent/child relationship mapping, see 24章例: 親/子供 for more information. Even more complex association mappings are covered in the next chapter.