为什么 merge() 会尝试 persist/validate 逆@OneToMany collection 中的实体?

Why does merge() attempt to persist/validate entities in an inverse @OneToMany collection?

我在 Hibernate (3.6.10) 中看到的行为与我对应该发生的事情的心智模型不符,我想知道这是否意味着存在错误,是有意识的决定打破了(otherwise-valid)的心智模型,或者我的心智模型是错误的。

我在 Parent 和 Child 之间有一个 bi-directional OneToMany/ManyToOne 关系,其中 Parent 有很多 Children 和 Child 是关系的所有者:

@Entity
public class Child {
    @ManyToOne
    @JoinColumn(name="PARENTID", nullable=false)
    private Parent parent;
}

@Entity
public class Parent {
    @OneToMany(mappedBy="parent", fetch=FetchType.LAZY)
    private List<Child> children;
}

还有我的测试(在使用 SpringJunit4ClassRunner 的 @Transactional JUnit 测试中):

@Test
public void test() {
    Child child = new Child();
    child.setName("TEST");
    childDao.saveOrUpdate(child);
    childDao.getSession.flush();
    childDao.getSession.evict(child);

    Parent parent = new Parent();
    parent.setChild(child);
    child.addParent(parent);

    childDao.merge(child);
}

我的心智模型表明,通过修改 Child 的 Parent 实体引用并保存 Child; Child 的 children collection 的更改被 Hibernate 忽略。

然而,如果我创建新的(瞬态)Child objects 并将它们添加到分离的 Parent object,当我调用 sessionFactory.merge(parent) 时, merge() 调用似乎检查了 Parent 的 children collection 并拒绝合并,因为那些 children 是暂时的,即使我的代码将迭代children 一旦 merge() 调用 returns。 (我用一个新的 Parent object 得到了相同的行为,它的自然键与数据库中已有的键匹配。)

Java Persistence With Hibernate 有一个项目符号与这个问题相关(来自 2007 年修正后的第五版第 413 页的底部):"Merging includes all value-typed properties and all additions and removals of elements to any collection." 如果语句是 "to any non-inverse collection",这将符合我的心智模型(即保留 saveOrUpdate() 会保留的所有更改),但在单词 "any" 中包含反向 collection 与我的心智模型冲突.

任何人都可以解释为什么 merge() 正在考虑 object 的逆状态 Collection(一个使用 mappedBy),即使 saveOrUpdate()会不理他们吗?是否有任何设置可以指示 merge() 忽略这些实体?

注意: 这个问题与 JPA2 and hibernate - why does merge store child entities whilst persist does not? 不同(尽管标题听起来完全一样),因为作者正在 尝试 进行级联(并且 persist() 不会发生这种情况),而我不希望自动级联行为。

merge 操作将传入的实体状态复制到附加实体或新加载的实体快照。

我添加了一个 test on GitHub 来复制你在 Hibernate 4.3.8 中的说法,它工作得很好。 Child 实体在反面被忽略,瞬态实体被简单地忽略。

两个瞬态实体:

doInTransaction(session -> {
    Post _post = new Post("Post");
    _post.getComments().add(new Comment());
    session.persist(_post);
    return _post;
});

分离个:

final Post post = doInTransaction(session -> {
    Post _post = new Post("Post");
    session.persist(_post);
    return _post;
});

doInTransaction(session -> {
    post.getComments().add(new Comment());
    session.merge(post);
});

被妥善保存,反面被忽略。

因为这在 3.6.10 下失败并在 4.3.8 下按预期工作,这似乎很可能只是 Hibernate 3.6.10 中的一个错误,在 3.6.10 和 4.3 之间的某个版本中得到修复。 8.