JPA/Hibernate - 防止在PreRemove处理程序中删除?

aro*_*oth 16 java hibernate jpa

问题标题基本上都说明了一切.在JPA/Hibernate中是否可以优雅地阻止从数据库中删除实体?我想要的是将实体标记为"隐藏"而不是实际删除它.

我还希望Cascade保留语义,这样如果我尝试删除拥有某个其他实体集合的实体,则拥有实体和其集合中的每个实体都被标记为隐藏,而我不需要任何额外的工作,超出实现@PreRemove阻止删除的处理程序并将实体标记为隐藏.

这是可能的,还是我需要弄清楚其他方法?

Vin*_*lds 12

在JPA/Hibernate中是否可以优雅地阻止从数据库中删除实体?

是的,只要你避免使用EntityManager.remove(entity)它是可能的.如果您确实使用EntityManager.remove(),那么JPA提供程序将使用相应的SQL DELETE语句标记要删除的对象,这意味着在标记要删除的对象后将无法使用优雅的解决方案.

在Hibernate中,您可以使用@SQLDelete@Where注释来实现此目的.但是,这对于JPA来说效果不佳,因为EntityManager.find()已知忽略@Where注释中指定的过滤器.

因此,仅JPA解决方案将涉及在实体类中添加标志(即列)以区分数据库中的逻辑删除实体与"活"实体.您需要使用适当的查询(JPQL和本机)来确保逻辑删除的实体在结果集中不可用.您可以使用@PreUpdate@PrePersist注释挂钩实体生命周期事件,以确保在持久化和更新事件时更新标志.同样,您需要确保不会调用该EntityManager.remove方法.

我建议使用@PreRemove注释来挂钩为删除实体而触发的生命周期事件,但使用实体监听器来防止删除由于下述原因而充满麻烦:

  • 如果需要SQL DELETE在逻辑意义上防止发生,则需要在同一事务中保留对象以重新创建它*.唯一的问题是,EntityManager在EntityListener中引用它并EntityManager.persist在侦听器中通过推理调用不是一个好的设计决策.基本原理非常简单 - 您最终可能会在EntityListener中获取不同的EntityManager引用,这只会导致应用程序中出现模糊和混乱的行为.
  • 如果您需要阻止SQL DELETE事务本身发生,那么您必须在EntityListener中抛出异常.这通常最终会回滚事务(特别是如果Exception是RuntimeException或声明为导致回滚的应用程序异常),并且不提供任何好处,因为整个事务将被回滚.

如果您可以选择使用EclipseLink而不是Hibernate,那么如果您定义适当的DescriptorCustomizer或使用AdditionalCriteria注释,则可能会有一个优雅的解决方案.这两个似乎都适用于EntityManager.removeEntityManager.find调用.但是,您可能仍需要编写JPQL或本机查询以考虑逻辑删除的实体.


*这在JPA Wikibook中有关级联持久性主题的概述:

如果删除一个对象以删除它,如果然后在对象上调用persist,它将恢复该对象,并且它将再次变为持久化.如果是有意的话,这可能是期望的,但JPA规范也要求级联持久化的这种行为.因此,如果删除对象,但忘记从级联持久关系中删除对它的引用,则将忽略删除.