22.2. 双方向一対多
Parent
から Child
への単純な <one-to-many>
関連から始めるとします。
<set name="children"> <key column="parent_id"/> <one-to-many class="Child"/> </set>
以下のコードを実行したとすると、
Parent p = .....; Child c = new Child(); p.getChildren().add(c); session.save(c); session.flush();
Hibernate は二つの SQL 文を発行します:
c
に対するレコードを生成するINSERT
p
からc
へのリンクを作成するUPDATE
これは非効率的なだけではなく、
parent_id
カラムにおいて NOT NULL
制約に違反します。コレクションのマッピングで not-null="true"
と指定することで、null 制約違反を解決することができます:
<set name="children"> <key column="parent_id" not-null="true"/> <one-to-many class="Child"/> </set>
しかしこの解決策は推奨できません。
この動作の根本的な原因は、
p
から c
へのリンク(外部キー parent_id
) は Child
オブジェクトの状態の一部とは考えられず、そのため INSERT
によってリンクが生成されないことです。つまり、解決策はリンクを Child
マッピングの一部にすることです。
<many-to-one name="parent" column="parent_id" not-null="true"/>
また
Child
クラスに parent
プロパティを追加する必要があります。
それでは
Child
エンティティがリンクの状態を制御するようになったので、コレクションがリンクを更新しないようにしましょう。それには inverse
属性を使います:
<set name="children" inverse="true"> <key column="parent_id"/> <one-to-many class="Child"/> </set>
以下のコードを使えば、新しい
Child
を追加することができます:
Parent p = (Parent) session.load(Parent.class, pid); Child c = new Child(); c.setParent(p); p.getChildren().add(c); session.save(c); session.flush();
SQL の
INSERT
文が一つだけが発行されるようになりました。
Parent
の addChild()
メソッドを作成することもできます。
public void addChild(Child c) { c.setParent(this); children.add(c); }
Child
を追加するコードはこのようになります:
Parent p = (Parent) session.load(Parent.class, pid); Child c = new Child(); p.addChild(c); session.save(c); session.flush();