兔八哥笔记15:Hibernate中的父子关系
邮箱:ltf_ty@163.net
本文翻译自Hibernate帮助文档的第九章。
Hibernate的新用户用Hibernate做的第一件事情很可能就是建立一个父子关系的数据模型。要达到这个目的,有2种途径:由于有不同的情况,最方便(特别是对于一个Hibernate的新用户来说)的途径是同时建立2个实体,一个是Parent,另一个是Child,然后建立一个从Parent到Child的<one-to-many>关联。另外一个途径是生命Child作为Parent的<composite-element>。Now, it turns out that default semantics of a one to many association (in Hibernate) are much less close to the usual semantics of a parent / child relationship than those of a composite element mapping.(这句没太读明白)。下面我们将向你展示怎样使用bidirectional one to many association with cascades去建立一个优雅且有效率的父子关系的模型,一点都不难!
9.1 关于集合的小结(A note about collections)
Hibernate集合被认为是实体自己的一个逻辑部分,而不是被包含的实体。这个区别是至关重要的!
当我们从一个集合中增加或删除一个对象时,集合所有者的版本号将会增加。
如果从集合中被删除的对象是一个值类型的实例(eg, a composite element),对象将不再是持久的,它的状态将从数据库中完全删除。同样,向集合中增加一个值类型的实例时,将会使它的状态立刻被持久化。
另一方面,如果一个实体从一个集合(a one-to-many or many-to-many association),中被removed,默认情况下,关联的实体将不被删除(deleted)。这个行为同下面的说法是完全一致的:其他实体的内部状态的改变不应该引起相关联的实体的消失(vanish)!同样,向集合中增加一个实体,在默认情况下,不会引起实体被持久化。
所以,向集合中增加一个实体时,仅仅是创建了一个两个实体之间的连接(link),而删除时,也仅仅删除了连接(link),这个规则适合各种各样的(all sorts of)情况。但也有根本不适合的情况:在父子关系中,Child的生命被绑定到Parent对象的生命期(lifecycle)中。
9.2 双向的one-to-many(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将转换成2个SQL语句:
² 一条为创建Child的insert语句
² 一条创建Parent到Child的关系的update语句
这样做不仅效率低下,而且违反了parent_id列的NOT NULL约束。
下面的动机是:从Parent到Child的连接(外键: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();
现在,只有一个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();