【发布时间】:2011-09-22 21:03:23
【问题描述】:
我想在交易失败后恢复。
现在,当然,在任何回滚之后,所有实体都会分离并且实体管理器会关闭。但是,UI 仍然包含分离的实体。显然我们不能直接丢弃用户的更改,所以我们想让他们重试(修复突出显示的验证错误,然后再次单击按钮)。
错误处理的一种方法是在提交失败后为每个托管对象调用合并到一个新的EntityManager,然后尝试提交新的EntityManager。一个问题可能是任何已分配的 id 或已分配或递增的乐观锁版本可能需要重置。此外,如果原来的 EntityManager 被 EXTENDED 扩展,任何正在使用的对象仍然会被分离,并且需要重新设置。
这个选项起初看起来很简单,直到我们不可避免地遇到那些预期的问题。某些服务可能会因各种原因触发刷新,这会增加 DB(已回滚)和 Java 实体(未回滚)中的 @Version 字段。下一个“保存”调用merge,它会抛出一个意外的OptimisticLockException。
是否有可靠的方法来“回滚”Java 实体 bean 中的版本字段?
好的,接缝很硬。我们有自己的@Versions 级联实体,所以手动操作似乎很脆弱。 (无论如何,我们如何才能可靠地知道原始(持久)版本?无法查询,因为其他用户可能同时成功更新实体;查询当前版本可能会破坏 oplocking!)
另一个更复杂的错误处理方法是始终使用非事务性 EntityManager。当需要提交时,会创建一个新的 EntityManager,将非事务性对象合并到其中,然后提交新的 EntityManager。如果提交失败,只有新的EntityManager的状态可能不一致,原来的EntityManager不受影响。这可以让问题得到纠正,并将 EntityManager 重新合并到另一个新的 EntityManager 中。如果提交成功,任何提交更改都可以合并回原始 EntityManager,然后可以继续正常使用。此解决方案需要相当多的开销,因此应仅在确实需要错误处理且 JPA 提供程序没有提供替代方案时使用。
这似乎合乎逻辑。 有没有人有使用两个 EntityManager(尤其是 Spring)实现这种恢复的经验?在尝试之前我应该注意哪些陷阱? 似乎每个服务和 DAO 现在都必须了解这两个实体管理器(对于 Spring,今天它们几乎与持久层无关)。 DAO 的“查找”操作使用一个 EM; 'update' 使用另一个。或者有单独的“读”和“写”DAO。哎哟。
我考虑过的其他选项包括:
- 在 UI 中使用 DTO,因此自动递增不会影响任何内容。丑陋。
- 将对
merge的调用移至任何组合操作的结束。仅在所有验证和状态更新成功后才附加实体。 “保存”服务不再merge(仅验证)似乎很奇怪。实际上,UI 将负责调用 DAO!这听起来很不寻常吗?
建议?谢谢 :-)
更新 我的架构包括:
- 由 UI (JSF) 更新的分离实体
- 实体 ID 不是自动生成的(预分配的 UUID 和/或业务密钥)
- 实体具有自动递增的
@Version字段以进行 oplocking - “保存”服务验证,调用
em.merge(JPA over Hibernate) - “流程”服务验证、应用业务逻辑、更新实体状态
- 可以组合服务。一个 UI 按钮就可以了
- (关于 UI 控制器的 Spring
@Transactional建议:开始) - 保存
- 流程 1
- 流程 2
- (
@Transactional: 提交)
- (关于 UI 控制器的 Spring
- 任何服务都可能抛出 validation exception (JSR 303),它会按预期回滚(消息显示在 UI 中)
【问题讨论】:
-
如果您的 UI 包含分离实体,那么这些实体扮演与 DTO 相同的角色:merge 仅将分离实体的状态复制到附加实体,分离实体保持原样(即它们的版本字段不应修改,无论事务是否已提交)。
-
@JB,马塞尔偷了你的答案。在那里发表评论
-
在这种情况下,使用
EntityManager.merge()是危险的。请参阅 JPA 2.1,“3.3.3 事务回滚”部分。它说:“[..] 版本属性的状态和生成的状态(例如,生成的主键)可能不一致。”正如本节所指出的,将这样一个分离的实体传递给合并操作“可能会失败”。 “3.2.7.1 合并分离实体状态”部分有解释:“实体使用的任何版本列都必须在合并操作期间由持久性运行时实现检查 [..]”。
标签: java hibernate jpa merge rollback