【问题标题】:JPA/Hibernate - Prevent deletion in PreRemove handler?JPA/Hibernate - 防止在 PreRemove 处理程序中删除?
【发布时间】:2011-07-11 05:12:00
【问题描述】:

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

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

这可能吗,还是我需要找出其他方法?

【问题讨论】:

  • 您是否尝试使用 JPA 模拟逻辑删除?
  • @Vineet Reynolds - 本质上是的。我希望保留记录,因为该应用程序与移动客户端同步,并且它需要能够告诉客户端已删除的内容。我可以通过在单独的表中保留已删除的所有内容的 id 列表以及删除它的时间戳来解决此问题,但我更希望能够覆盖删除行为,以便它只是隐藏而不是删除它们。

标签: java hibernate jpa


【解决方案1】:

在 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 规范也要求这种行为用于级联持久化。因此,如果您删除了一个对象,但忘记从级联持久关系中删除对它的引用,则删除将被忽略。

【讨论】:

  • 啊,我一直希望能够使用EntityManager.remove() 让它工作。但是正如您所说,没有可靠的方法可以在侦听器中获取正确的 EntityManager 实例。按照您的建议,我已经走了很长一段路,并用更新替换了我的删除,这很有效。不过,我希望 JPA 能够为这个用例提供更优雅的解决方案。
  • 是的,我同意应该在 JPA 中合并一个更优雅的解决方案,以便 em.remove() 可以映射到本机 SQL 查询或 JPQL 语句,此自定义行为在适当的位置 - 在实体上或其他地方。
猜你喜欢
  • 1970-01-01
  • 2021-12-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多