JPA:删除 WHERE 不会删除子项并引发异常

2022-09-01 11:54:22

我正在尝试从JPQL查询中删除大量行。MOTHER

该类定义如下:Mother

@Entity
@Table(name = "MOTHER")
public class Mother implements Serializable {

    @OneToMany(cascade = CascadeType.ALL, mappedBy = "mother", 
               orphanRemoval = true)
    private List<Child> children;    
}

@Entity
@Table(name = "CHILD")
public class Child  implements Serializable {

    @ManyToOne
    @JoinColumn(name = "MOTHER_ID")
    private Mother mother;    
}

如您所见,该类具有“子级”,并且在执行以下查询时:Mother

String deleteQuery = "DELETE FROM MOTHER WHERE some_condition";
entityManager.createQuery(deleteQuery).executeUpdate();

抛出异常:

ERROR - ORA-02292: integrity constraint <constraint name> violated - 
                   child record found

当然,我可以先选择要删除的所有对象,然后将它们检索到列表中,然后再循环访问以删除所有检索到的对象,但是这种解决方案的性能将非常糟糕!

那么,有没有办法利用前面的映射来有效地删除所有对象以及与它们关联的所有对象,而无需首先为所有子级编写查询?MotherChild


答案 1

DELETE(和 INSERT)不会通过 JPQL 查询中的关系进行级联。这在规范中拼写得很清楚:

删除操作仅适用于指定类及其子类的实体。它不会级联到相关实体。

幸运的是,通过实体管理器执行(当定义了级联属性时)持久化和删除。

您可以执行的操作:

  • 获取应删除的所有母实体实例。
  • 对于它们中的每一个,调用EntityManager.remove()。

代码是这样的:

String selectQuery = "SELECT m FROM Mother m WHERE some_condition";  
List<Mother> mothersToRemove = entityManager
    .createQuery(selectQuery)
    .getResultStream()
    .forEach(em::remove);

答案 2

您是否尝试过使用 session.delete() 或等效的 EntityManager.remove()

当您使用 HQL 删除语句发出查询时,您可能绕过了 Hibernate 的级联机制。看看这个JIRA问题:HHH-368

您将有可能通过以下方式实现这一目标:

Mother mother = session.load(Mother.class, id);
// If it is a lazy association, 
//it might be necessary to load it in order to cascade properly
mother.getChildren(); 
session.delete(mother);

我现在不确定是否有必要初始化集合以使其正确级联。


推荐