10

我有以下带注释的 Hibernate 实体类:

@Entity
public class Cat {
    @Column(name = "ID") @GeneratedValue(strategy = GenerationType.AUTO) @Id
    private Long id;

    @OneToMany(mappedBy = "cat", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    private Set<Kitten> kittens = new HashSet<Kitten>();

    public void setId(Long id) { this.id = id; }
    public Long getId() { return id; }
    public void setKittens(Set<Kitten> kittens) { this.kittens = kittens; }
    public Set<Kitten> getKittens() { return kittens; }
}

@Entity
public class Kitten {
    @Column(name = "ID") @GeneratedValue(strategy = GenerationType.AUTO) @Id
    private Long id;

    @ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    private Cat cat;

    public void setId(Long id) { this.id = id; }
    public Long getId() { return id; }
    public void setCat(Cat cat) { this.cat = cat; }
    public Cat getCat() { return cat; }
}

我的意图是 Cat 和 Kitten 之间的双向一对多/多对一关系,其中 Kitten 是“拥有方”。

我想要发生的事情是,当我创建一个新的 Cat,然后是一个引用该 Cat 的新 Kitten 时,我的 Cat 上的 Set of kittens 应该包含新的 Kitten。但是,这不会在以下测试中发生:

@Test
public void testAssociations()
{
    Session session = HibernateUtil.getSessionFactory().getCurrentSession();
    Transaction tx = session.beginTransaction();

    Cat cat = new Cat();
    session.save(cat);

    Kitten kitten = new Kitten();
    kitten.setCat(cat);
    session.save(kitten);

    tx.commit();

    assertNotNull(kitten.getCat());
    assertEquals(cat.getId(), kitten.getCat().getId());
    assertTrue(cat.getKittens().size() == 1); // <-- ASSERTION FAILS
    assertEquals(kitten, new ArrayList<Kitten>(cat.getKittens()).get(0));
}

即使在重新查询 Cat 之后,Set 仍然是空的:

// added before tx.commit() and assertions
cat = (Cat)session.get(Cat.class, cat.getId());

我对 Hibernate 的期望是不是太高了?或者是我自己管理收藏的负担?(注释)文档没有表明我需要在父对象上创建便利addTo*/方法。removeFrom*

有人可以告诉我我对 Hibernate 与这种关系的期望是什么吗?或者,如果没有别的,请指出正确的 Hibernate 文档,它告诉我我应该期待在这里发生什么。

我需要做什么才能使父集合自动包含子实体?

4

2 回答 2

15

它不会自动添加它。你必须自己添加它。

我也不会直接打电话Kitten.setCat()。典型的模式是把一个方法放在Cat这样的地方:

public void addKitten(Kitten kitten) {
  if (kittens == null) {
    kittens = new HashSet<Kitten>();
  }
  kittens.add(kitten);
  kitten.setCat(this);
}

然后简单地调用:

cat.addKitten(kitten);
于 2010-05-27T13:07:38.877 回答
4

在使用双向关联时,您必须处理“链接”的两侧,并且正如@cletus 所建议的那样,为此使用防御性链接管理方法是很常见的。来自 Hibernate Core 文档:

1.2.6。工作双向链接

首先,请记住 Hibernate 不会影响正常的 Java 语义。在单向示例中,我们如何在 Person 和 Event 之间创建链接?您将 Event 实例添加到 Person 实例的事件引用集合中。如果要使此链接双向,则必须在另一端执行相同操作,方法是在 Event 中添加对集合的 Person 引用。这种“在两边设置链接”的过程对于双向链接是绝对必要的。

许多开发人员进行防御性编程并创建链接管理方法以正确设置双方(例如,在 Person 中):

protected Set getEvents() {
    return events;
}

protected void setEvents(Set events) {
    this.events = events;
}

public void addToEvent(Event event) {
    this.getEvents().add(event);
    event.getParticipants().add(this);
}

public void removeFromEvent(Event event) {
    this.getEvents().remove(event);
    event.getParticipants().remove(this);
}

集合的 get 和 set 方法现在受到保护。这允许同一包和子类中的类仍然访问方法,但防止其他人直接更改集合。对另一侧的集合重复上述步骤。

更多参考资料

于 2010-05-27T19:31:46.127 回答