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

假设我们要实现一个简单的从Parent到Child的<one-to-many>关联。

  1. <set name="children">
  2. <key column="parent_id"/>
  3. <one-to-many class="Child"/>
  4. </set>

如果我们运行下面的代码

  1. Parent p = .....;
  2. Child c = new Child();
  3. p.getChildren().add(c);
  4. session.save(c);
  5. session.flush();

Hibernate会产生两条SQL语句:

  • 一条INSERT语句,为c创建一条记录

  • 一条UPDATE语句,创建从pc的连接

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

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

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

这种现象的根本原因是从pc的连接(外键parent_id)没有被当作Child对象状态的一部分,因而没有在INSERT语句中被创建。因此解决的办法就是把这个连接添加到Child的映射中。

  1. <many-to-one name="parent" column="parent_id" not-null="true"/>

(我们还需要为类Child添加parent属性)

现在实体Child在管理连接的状态,为了使collection不更新连接,我们使用inverse属性。

  1. <set name="children" inverse="true">
  2. <key column="parent_id"/>
  3. <one-to-many class="Child"/>
  4. </set>

下面的代码是用来添加一个新的Child

  1. Parent p = (Parent) session.load(Parent.class, pid);
  2. Child c = new Child();
  3. c.setParent(p);
  4. p.getChildren().add(c);
  5. session.save(c);
  6. session.flush();

现在,只会有一条INSERT语句被执行!

为了让事情变得井井有条,可以为Parent加一个addChild()方法。

  1. public void addChild(Child c) {
  2. c.setParent(this);
  3. children.add(c);
  4. }

现在,添加Child的代码就是这样

  1. Parent p = (Parent) session.load(Parent.class, pid);
  2. Child c = new Child();
  3. p.addChild(c);
  4. session.save(c);
  5. session.flush();