orphanRemoval 在休眠中无法正常工作:从列表中插入和删除的元素保留在数据库中
orphanRemoval not working properly in hibernate : Element inserted and removed from list is persisted in DB
我们有一个问题听起来像是 Hibernate 处理 orphanRemoval=true 一个 oneToMany[ 的错误=40=] 列表(带索引).
这是简化的映射:
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;
}
}
和 child class :
public class ChildClass {
[...]
@ManyToOne
@JoinColumn(nullable = false, name = "parent_id")
public ParentClass getParent() {
return parent;
}
}
给定此映射,以下情况将失败:
- 从 DB
中获取一个 Parent
- 给它加一个child
- 从 DB(不是同一个实体)获取其他东西,产生部分刷新
- 从 parent
中删除 child
- 离开交易
这是代码
@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);
}
在这种情况下,child 分配会插入到数据库中,并且不会在事务结束时删除。
但是,如果我们不执行步骤 3),child 分配不会正确地保存在 DB
中
这是一个错误吗?错误的映射?
有解决方法吗?
是的,这是一个错误。我打赌你没有使用最新版本的 Hibernate (4.3.8),并且它与这个问题相关(如果不是同一个问题):[HHH-9330] orphanRemoval=true does not work in bidirectional relationships (without cascading) 即使你使用最新版本,这个错误也可能仍然存在。如果是这样,请报告它,然后将 URL 报告给此处某处的错误。解决方法:仅在确定必须保留子项时才尝试添加子项,或者也尝试将子项中的父项设置为 null:
child.setParent(null);
另外,作为另一种解决方法,您可以尝试使用 Hibernate 会话,而不是 EntityManager(因为它写在 Hibernate 的论坛中)。
删除Child时需要设置关联的两边:
child.setParent(null);
parent.getChildren().remove(child);
在你的情况下,孤儿移除是不够的。如果一对多方是单向关联,那么您可以简单地从列表中删除子实体,删除操作将传播到子实体。
当你有一个双向关联时,你需要让双方同步。这就是为什么您需要在删除时取消子实体与父实体的关联。
有一个 JPA 注释 @PreRemove
可能对您的特定情况有所帮助。尝试将其添加到子实体,以便它可以从父实体中删除自身:
@PreRemove
protected void beforeRemove(){
parent.getChildren().remove(this);
}
我们有一个问题听起来像是 Hibernate 处理 orphanRemoval=true 一个 oneToMany[ 的错误=40=] 列表(带索引).
这是简化的映射:
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;
}
}
和 child class :
public class ChildClass {
[...]
@ManyToOne
@JoinColumn(nullable = false, name = "parent_id")
public ParentClass getParent() {
return parent;
}
}
给定此映射,以下情况将失败:
- 从 DB 中获取一个 Parent
- 给它加一个child
- 从 DB(不是同一个实体)获取其他东西,产生部分刷新
- 从 parent 中删除 child
- 离开交易
这是代码
@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);
}
在这种情况下,child 分配会插入到数据库中,并且不会在事务结束时删除。
但是,如果我们不执行步骤 3),child 分配不会正确地保存在 DB
中这是一个错误吗?错误的映射? 有解决方法吗?
是的,这是一个错误。我打赌你没有使用最新版本的 Hibernate (4.3.8),并且它与这个问题相关(如果不是同一个问题):[HHH-9330] orphanRemoval=true does not work in bidirectional relationships (without cascading) 即使你使用最新版本,这个错误也可能仍然存在。如果是这样,请报告它,然后将 URL 报告给此处某处的错误。解决方法:仅在确定必须保留子项时才尝试添加子项,或者也尝试将子项中的父项设置为 null:
child.setParent(null);
另外,作为另一种解决方法,您可以尝试使用 Hibernate 会话,而不是 EntityManager(因为它写在 Hibernate 的论坛中)。
删除Child时需要设置关联的两边:
child.setParent(null);
parent.getChildren().remove(child);
在你的情况下,孤儿移除是不够的。如果一对多方是单向关联,那么您可以简单地从列表中删除子实体,删除操作将传播到子实体。
当你有一个双向关联时,你需要让双方同步。这就是为什么您需要在删除时取消子实体与父实体的关联。
有一个 JPA 注释 @PreRemove
可能对您的特定情况有所帮助。尝试将其添加到子实体,以便它可以从父实体中删除自身:
@PreRemove
protected void beforeRemove(){
parent.getChildren().remove(this);
}