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

时间:2011-07-11 05:12:00

标签: java hibernate jpa

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

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

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

1 个答案:

答案 0 :(得分:12)

  

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

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

在Hibernate中,您可以使用@SQLDelete and @Where annotations实现此目的。但是,这不适用于JPA,因为已知EntityManager.find()会忽略@Where注释中指定的过滤器。

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

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

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

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


* JPA Wikibook on the topic of cascading Persist

中概述了这一点
  

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