22.2. 双方向一対多
Parent から Child への単純な <one-to-many> 関連から始めるとします。
<set name="children">
<key column="parent_id"/>
<one-to-many class="Child"/>
</set>
<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();
Parent p = .....;
Child c = new Child();
p.getChildren().add(c);
session.save(c);
session.flush();
Hibernate は二つの SQL 文を発行します:
cに対するレコードを生成するINSERTpから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>
<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"/>
<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>
<set name="children" inverse="true">
<key column="parent_id"/>
<one-to-many class="Child"/>
</set>
以下のコードを使えば、新しい
Child を追加することができます:
SQL の
INSERT 文が一つだけが発行されるようになりました。
Parent の addChild() メソッドを作成することもできます。
public void addChild(Child c) {
c.setParent(this);
children.add(c);
}
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();
Parent p = (Parent) session.load(Parent.class, pid);
Child c = new Child();
p.addChild(c);
session.save(c);
session.flush();