【发布时间】:2014-05-17 03:35:56
【问题描述】:
我正在使用 Hibernate Envers 来保存对象历史记录。在某些时候,我们想要捕获对象图状态的快照——我们可以通过知道相应的 Envers 修订来做到这一点,然后我们将其存储在审计记录中。
但是我们有一个问题。父对象在我们创建和存储其子审计记录的同一事务中更新 - 完成 Envers 修订。我们可以得到最新的版本:
Number revision = reader.getRevisionNumberForDate(new Date(Long.MAX_VALUE));
或创建一个新版本:
Number revision = reader.getCurrentRevision(DefaultRevisionEntity.class, true).getId();
并使用其中之一,但父级的 commit 总是在那之后发生。这就是 Envers 增加修订的时候。因此,我们实际需要在审计记录中引用的修订总是高于存储的值。在最简单的情况下,我们获取并存储修订版本 N,但我们需要的父版本存储为 N + 1。
可以通过以下方式获得 AuditReader 阅读器参考:
JpaTransactionManager transactionManager; // injected
EntityManagerFactory emf = transactionManager.getEntityManagerFactory();
EntityManager entityManager = emf.createEntityManager();
AuditReader reader = AuditReaderFactory.get(entityManager);
我们正在使用 Spring 3 @Transactional 注释和 Hibernate 4.2。
最小类图:
Parent.class
int version // for hibernate optimistic locking
String revisionName
List<AuditChild> audits
AuditChild.class
int enversRevision // use to retrieve previous graphs of parent
我尝试了许多方法来强制先提交父项,其中包括:
- 使用 @Transactional(propagation = Propagation.REQUIRES_NEW) 在多个方法中拆分代码
- entityManager.flush() 的显式调用;
我尝试过的一切要么没有效果,要么导致了其他问题。我很高兴听到对其他人有用的解决方案。谢谢。
【问题讨论】:
-
修订号对于一个事务应该是唯一的,你确定
reader.getCurrentRevision(DefaultRevisionEntity.class, true).getId()不起作用吗?您应该需要引用当前事务的“当前”版本。 -
@adamw 是的,它创建了一个新修订版。但是,当我的封闭事务已提交时,*_AUD 表中相关更新行的实际修订> n(当数据库没有其他更新与此 tx 重叠时,n + 1)。因此,
getCurrentRevision()返回的对象上的 ID 不是当前 Tx 提交时应用的 ID。我的解决方法是使用虚拟 (-1) 修订版创建并在第二个事务中回读并更新我的修订版参考。 (环境:Hibernate Envers 4.2、Spring 3.2 @Transactional 注释事务;DB2 9.1)。 -
嗯,如果你在 TX 中调用 getCurrentRevision(),你应该得到该交易的修订。中间有flush()es吗?虽然这不应该真的很重要。
-
@adamw 感谢您的回复。我意识到我需要为完整的复制案例提供代码才能进一步研究。但是我的两个 Tx 解决方法现在已经足够好了。仅供参考,我也在 community.jboss.org/thread/171696 上评论过这个问题
-
看看这篇博文:azagorneanu.blogspot.com/2013/06/…,它可以通过注册提交后回调使你的 2 TX 解决方法更简单。
标签: hibernate spring-transactions hibernate-envers