21.2. 双向的一对多关系(Bidirectional one-to-many)
假设我们要实现一个简单的从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语句:
一条
INSERT
语句,为c
创建一条记录一条
UPDATE
语句,创建从p
到c
的连接
这样做不仅效率低,而且违反了列parent_id
非空的限制。我们可以通过在集合类映射上指定not-null="true"
来解决违反非空约束的问题:
<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
在管理连接的状态,为了使collection不更新连接,我们使用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();
现在,只会有一条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();