Java 孤立删除在hibernate中无法正常工作:从列表中插入和删除的元素在数据库中保持不变
我们有一个问题,听起来像是Hibernate中的一个bug在处理孤儿删除问题=对于oneToMany列表(带索引) 以下是简化的映射:Java 孤立删除在hibernate中无法正常工作:从列表中插入和删除的元素在数据库中保持不变,java,hibernate,jpa,orm,hibernate-mapping,Java,Hibernate,Jpa,Orm,Hibernate Mapping,我们有一个问题,听起来像是Hibernate中的一个bug在处理孤儿删除问题=对于oneToMany列表(带索引) 以下是简化的映射: public class ParentClass { [...] @OneToMany(cascade = ALL, mappedBy = "parent", orphanRemoval = true) @OnDelete(action = OnDeleteAction.CASCADE) @Fetch(FetchMode.JOIN) @O
public class ParentClass {
[...]
@OneToMany(cascade = ALL, mappedBy = "parent", orphanRemoval = true)
@OnDelete(action = OnDeleteAction.CASCADE)
@Fetch(FetchMode.JOIN)
@OrderColumn(name = "pos", nullable = false)
public List<ChildClass> getChildren() {
return children;
}
}
鉴于此映射,以下场景将失败:
@Transactional
public void test() {
// 1)
ParentClass parent = entityManager.find(ParentClass.class, "some-id");
// 2)
ChildClass child = new ChildClass(parent);
parent.getChildren().add(child);
// 3)
entityManager.find(SomethingElse.class, "2");
// 4)
parent.getChildren().remove(child);
}
在这种情况下,子分配被插入到DB中,而不是在事务结束时删除
但是,如果不执行步骤3),则子分配不会正确地持久化到数据库中
那是虫子吗?错误的映射?
有解决方法吗?是的,这是一个bug。我打赌你没有使用Hibernate的最新版本(4.3.8),而且它与这个问题相关(如果不是同一个问题):即使你使用最新版本,这个bug仍然可能存在。如果是这样,请报告它,然后在这里的某个地方向bug报告URL。解决方法:仅当确定必须持久化子项时,才尝试添加该子项,或者尝试将子项中的父项设置为null:
child.setParent(空)代码>
另外,作为另一种解决方法,您可以尝试使用Hibernate会话,而不是EntityManager(正如在Hibernate论坛中所写的那样)。删除孩子时,您需要设置关联的双方:
child.setParent(null);
parent.getChildren().remove(child);
在你的情况下,孤儿的移除是不够的。如果一对多端是单向关联,那么您只需从列表中删除子对象,删除操作就会传播到子实体
当您具有双向关联时,您需要使双方同步。这就是为什么在删除时需要将子实体与父实体解除关联。有一个JPA注释@PreRemove
,在您的特定情况下可能会有所帮助。尝试将其添加到子实体,以便它可以从父实体中删除自身:
@PreRemove
protected void beforeRemove(){
parent.getChildren().remove(this);
}
恐怕双向关系中的orpahnRemoval功能也应该起作用。我在回答中发布的链接是讨论内容的地方。谢谢,我只是尝试了一下,在4)处将parent设置为null,而不是DELETE,in生成一个更新,parentId为null,这是DB禁止的(我们有显式的notnull约束)。顺便说一句,我们使用Hibernate 4.2.2,我明白了,为什么不调用em.remove(child)代码>相反(当然是workadound)?是的,这就是我们最终要做的。只是目前我们依赖于这样一种断言,即Hibernate可以很好地遍历模型的状态,而无需每次删除某些内容时都显式地调用em.remove()。这也带来了一些设计上的不一致性:我们不希望在低级业务服务中使用任何与持久性相关的服务。我担心,只要您不更新到最新版本(请注意,版本4.3.7似乎也会受到影响,因为此错误更正是最近的),您就必须执行这些操作(您至少应该在代码中添加一条注释,并带有bug链接)。我的意思是,你最好保持这段代码的原样,不要有引入新错误/不希望出现的行为的风险,因为它至少现在起作用。如果你想继续抱怨JPA,那么你不允许访问/更改任何关系。你只允许更改自己的属性,如字符串、原语……但不允许更改任何关系。
@PreRemove
protected void beforeRemove(){
parent.getChildren().remove(this);
}