2.2.5.3. Collections コレクション
<2.2.5.3. Collections | 目次 | 2.2.5.3.3. Many-to-many>
2.2.5.3.1. Overview 概要
Collection、List(順序ありでもなしでも)、Map、Set にマッピングできるよ。EJB3 仕様では、@javax.persistence.OrderBy アノテーションを使って順序つきリストにマップする仕方を定義しているよ。このアノテーションは、順序をつけるためのカンマで区切られた(対象エンティティの)プロパティのリストをパラメータに取るよ。(たとえば、firstname asc、age desc)この指定が空の場合、id で並べられるよ。インデックスコレクションの詳細は、Hibernate Annotation Extensions を参照してね。EJB3 は @MapKey(name="myProperty") を使って、対象エンティティのプロパティをキーとしたマップにマッピングすることもできるよ。(myPropertyは対象エンティティのプロパティ名) プロパティ名なしで @MapKey を使うと主キーが使われるよ。マップのキーはプロパティが指しているのと同じカラムを使うよ。マップキーを持つカラムがないと、マップキーは対象のプロパティをあらわすよ*1。一度だけ読み込まれるということを知っておいて。プロパティの値を変更したら、キーはもうプロパティと同期してないよ。キーは自動的には更新されないよ。(詳細は Hibernate Annotation Extensions を見てね) 多くの人が
Hibernate にはいくつかのコレクションの概念があるよ。
Table 2.1. Collections semantics コレクションの意味
意味 | java表現 | アノテーション |
---|---|---|
袋の意味 | java.util.List, java.util.Collection | @org.hibernate.annotations.CollectionOfElements or @OneToMany or @ManyToMany |
主キー付きの袋 | java.util.List, java.util.Collection | (@org.hibernate.annotations.CollectionOfElements or @OneToMany or @ManyToMany) and @CollectionId |
リスト | java.util.List | (@org.hibernate.annotations.CollectionOfElements or @OneToMany or @ManyToMany) and @org.hibernate.annotations.IndexColumn |
集合 | java.util.Set | @org.hibernate.annotations.CollectionOfElements or @OneToMany or @ManyToMany |
写像 | java.util.Map | (@org.hibernate.annotations.CollectionOfElements or @OneToMany or @ManyToMany) and (nothing or @org.hibernate.annotations.MapKey/MapKeyManyToMany for true map support, OR @javax.persistence.MapKey |
特に、 @org.hibernate.annotations.IndexColumn を含まない java.util.List は、袋と考えられているよ。
プリミティブ、コアタイプ、または埋め込みオブジェクトのコレクションは EJB3 ではサポートされてないけど、Hibernate ではできるよ。(Hibernate Annotation Extensions 参照)
@Entity public class City { @OneToMany(mappedBy="city") @OrderBy("streetName") public List<Street> getStreets() { return streets; } ... } @Entity public class Street { public String getStreetName() { return streetName; } @ManyToOne public City getCity() { return city; } ... } @Entity public class Software { @OneToMany(mappedBy="software") @MapKey(name="codeName") public Map<String, Version> getVersions() { return versions; } ... } @Entity @Table(name="tbl_version") public class Version { public String getCodeName() {...} @ManyToOne public Software getSoftware() { ... } ... }
コレクションがロードされたときに、City は streetName でソートされた Street のコレクションを持つよ。Software は codeName をキーにした Version のマップを持つよ。
コレクションがジェネリックじゃないなら、targetEntity を定義しないといけないよ。これは、ターゲットエンティティのクラス名を取る、アノテーションの属性だよ。
2.2.5.3.2. One-to-many 一対多
一対多関連は @OneToMany アノテーションでプロパティレベルに定義されるよ。一対多関連は双方向かもしれないよね。
2.2.5.3.2.1. Bidirectional 双方向
EJB3仕様において、多対一は大体双方向関連の親側なので、一対多関連は @OneToMany(mappedBy=...) でアノテートされるよ。
@Entity public class Troop { @OneToMany(mappedBy="troop") public Set<Soldier> getSoldiers() { ... } @Entity public class Soldier { @ManyToOne @JoinColumn(name="troop_fk") public Troop getTroop() { ... }
Troop は Solidier の troop プロパティ で、双方向の一対多関連を持っているよ。mappedBy 側に物理マッピングを定義してはいけないよ。
親側のような一対多側で、双方向の一対多をマッピングするために、mappedBy を除いて、多対一側の @JoinClumn の insertable、updatable を false にしないといけないよ。この方法は、明らかに最適ではなくて、同じ 追加のUPDATE分を生成するよ。
@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() { ... }
2.2.5.3.2.2. Unidirectional 単方向
親エンティティの外部キーを使った単方向の一対多は、一般的じゃなくて、ほんとにお勧めしないよ。このような場合は、次で説明する結合テーブルを使うことを強くお勧めするよ。この種類の関連は @JoinColumn で定義するよ。
@Entity public class Customer implements Serializable { @OneToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER) @JoinColumn(name="CUST_ID") public Set<Ticket> getTickets() { ... } @Entity public class Ticket implements Serializable { ... //no bidir }
Customer は、結合カラム CUST_ID を使って、Ticket へ一方向の関連を記述しているよ。
2.2.5.3.2.3. Unidirectional with join table 結合テーブルを使った単方向
結合テーブルを使った単方向一対多が好ましいよ。この関連は @JoinTable を使うよ。
@Entity public class Trainer { @OneToMany @JoinTable( name="TrainedMonkeys", joinColumns = @JoinColumn( name="trainer_id"), inverseJoinColumns = @JoinColumn( name="monkey_id") ) public Set<Monkey> getTrainedMonkeys() { ... } @Entity public class Monkey { ... //no bidir }
Trainer は、TrainedMonkeys結合テーブル(Trainerへのtrainer_id外部キーと、Monkeyへの外部キー monky_idをもつ)を使って Monkey に一方向関連を記述しているよ。
2.2.5.3.2.4. Defaults デフォルト
物理マッピングが何もないと、結合テーブルによる一方向一対多が使われるよ。テーブル名は 親テーブル名、_、もう一方のテーブル名を繋げたものになるよ。親テーブルを参照する外部キー名は、親テーブル、_、親テーブルの主キー名を連結したものになるよ。もう一方のテーブルを参照する外部キー名は、親プロパティ名、_、もう一方のテーブルの主キー名になるよ。もう一方のテーブルを参照する外部キーにユニーク制約がつけられるよ。
@Entity public class Trainer { @OneToMany public Set<Tiger> getTrainedTigers() { ... } @Entity public class Tiger { ... //no bidir }
Trainer は Trainer_Tiger結合テーブル(Trainer をさす trainer_id 外部キーと、Monkey をさす trainedTigers_id 外部キーをもつ )を使って、Tiger へ単方向関連を定義するよ。<2.2.5.3. Collections | 目次 | 2.2.5.3.3. Many-to-many>
*1:意味不明訳・・・