orphanRemove在hibernate中无法正常工作:从列表中插入和删除的元素被持久化在DB中



我们在Hibernate中遇到了一个问题,听起来像是一个bug,它处理oneToManyList(带索引)的orphanRemoval=true

以下是简化的映射:

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;
  }
}

儿童班:

public class ChildClass {
    [...]
    @ManyToOne
    @JoinColumn(nullable = false, name = "parent_id")
    public ParentClass getParent() {
        return parent;
    } 
}

给定此映射,以下场景将失败:

  1. 从DB获取父级
  2. 向其中添加子项
  3. 从DB(不是同一个实体)获取其他内容,生成部分刷新
  4. 从父对象中删除子对象
  5. 退出交易

这是代码

@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),则子分配不会正确地持久化在DB中

那是个虫子吗?一个错误的映射?有解决办法吗?

是的,这是一个错误。我敢打赌,你没有使用Hibernate的上一个版本(4.3.8),它与这个问题有关(如果不是同一个问题的话):[HHHH-9330]orphanRemoval=true在双向关系中不起作用(没有级联)即使你使用上一个版本,这个bug也可能仍然存在。如果是,请报告它,然后在这里的某个地方报告错误的URL。解决方法:只有在确定必须持久化时才尝试添加子项,或者也尝试将子项中的父项设置为空:

child.setParent(null);

作为另一种解决方法,您可以尝试使用Hibernate会话,而不是EntityManager(正如Hibernate论坛中所写的那样)。

删除Child:时需要设置关联的两侧

child.setParent(null);
parent.getChildren().remove(child);  

在您的情况下,孤立删除是不够的。如果一对多是单向关联,那么您可以简单地从列表中删除Child,并且删除将传播到Child实体。

当你有双向关联时,你需要让双方同步。这就是为什么在删除时需要将子实体与父实体取消关联。

有一个JPA注释@PreRemove,在您的特定情况下可能会有所帮助。尝试将其添加到子实体,以便它可以从父实体中删除:

@PreRemove
protected void beforeRemove(){
    parent.getChildren().remove(this);
}

最新更新