22.2. 双向的一对多关系(Bidirectional one-to-many)

优质
小牛编辑
120浏览
2023-12-01

假设我们要实现一个简单的从 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 语句,创建从 pc 的连接

这样做不仅效率低,而且违反了 parent_idparent_id 非空的限制。我们可以通过在集合类映射上指定 not-null="true" 来解决违反非空约束的问题:


<set name="children">
    <key column="parent_id" not-null="true"/>
    <one-to-many class="Child"/>
</set
>

然而,这并非是推荐的解决方法。

这种现象的根本原因是从 pc 的连接(外键 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();