[英]Hibernate session flush on rollback fails with custom JTA Transaction Manager
I am using Hibernate 4.2.2.Final within a custom application server, providing a custom implementation of JTA Transaction Manager to manage transactional context. 我在自定义应用程序服务器中使用Hibernate 4.2.2.Final,它提供了JTA Transaction Manager的自定义实现来管理事务上下文。 We use the DAO pattern to abstract away the details of managing hibernate sessions from the use and transparently inject transactional context when needed.
我们使用DAO模式从使用中抽象化管理休眠会话的细节,并在需要时透明地注入事务上下文。
Here is how we configure the session factory: 这是我们配置会话工厂的方式:
TransactionManager transactionManager = ((BasicManagedDataSource) dataSource).getTransactionManager();
if (transactionManager != null) {
properties.put(AvailableSettings.CURRENT_SESSION_CONTEXT_CLASS, "jta");
properties.put(AvailableSettings.JTA_PLATFORM, new MyJtaPlatform(transactionManager));
properties.put(AvailableSettings.TRANSACTION_STRATEGY, new JtaTransactionFactory());
}
Within the DAO object, when user retrieves a session within a transactional context, we register a listener to be notified of the transaction's completion so that we can flush the session: 在DAO对象中,当用户在事务上下文中检索会话时,我们注册一个侦听器以通知事务完成,以便我们可以刷新会话:
this.session = sessionFactory.getCurrentSession();
session.setFlushMode(FlushMode.MANUAL);
if (!flushSynchronizationMap.containsKey(session)) {
Synchronization flushSynchronization = new Synchronization() {
@Override
public void beforeCompletion() {
log.beforeTxCompletes(session);
if (session.isOpen()) {
log.flushing(session);
session.flush();
session.close();
}
}
@Override
public void afterCompletion(int status) {
flushSynchronizationMap.remove(session);
log.afterTxCompletes(session);
}
};
try {
currentJtaTransaction.registerSynchronization(flushSynchronization);
flushSynchronizationMap.put(session, flushSynchronization);
} catch (Exception ex) {
throw new RuntimeException("Could not register Flush Synchronization", ex);
}
}
However the following test fails, the assert at end of test expects table to be empty, but it is not: 但是,以下测试失败,测试结束时的断言期望表为空,但不是:
@Test
public void canRollbackTransaction() throws Exception {
List<SampleData> data = dao.findAll(SampleData.class);
assertThat(data).describedAs("size before insert should be 0").hasSize(0);
manager.begin();
dao.saveOrUpdate(new SampleData(12.0, "Hello World"));
dao.saveOrUpdate(new SampleData(13.0, "Hello Brave World"));
manager.rollback();
dbUnitSupport.assertDB(table("SAMPLES").columns("LABEL").dataSet());
}
I can see the transaction listener is called and session is flushed, but it looks as if the flush happened too late... 我可以看到调用了事务侦听器并刷新了会话,但是看上去刷新发生得太晚了……
When fixing the test with an explicit flush of the current session, it passses: 使用当前会话的显式刷新修复测试时,它通过:
@Test
public void canRollbackTransaction() throws Exception {
List<SampleData> data = dao.findAll(SampleData.class);
assertThat(data).describedAs("size before insert should be 0").hasSize(0);
manager.begin();
dao.saveOrUpdate(new SampleData(12.0, "Hello World"));
dao.saveOrUpdate(new SampleData(13.0, "Hello Brave World"));
// under the hood, flush current session
dao.flush();
manager.rollback();
dbUnitSupport.assertDB(table("SAMPLES").columns("LABEL").dataSet());
}
I traced the issue down to debug-level logs and cannot understand what's different: In the log, flush appears to be done correctly before the transaction is rollbacked. 我将问题归结为调试级别的日志,无法理解有什么不同:在日志中,刷新似乎在回滚事务之前已正确完成。
What am I missing? 我想念什么? I could not find examples out there which implements precisely this scenario (I may have not searched correctly...) and I think I follow what's documented in the Hibernate documentation.
我找不到合适的示例来实现这种情况(我可能没有正确搜索...),我想我会遵循Hibernate文档中记录的内容。
I added the following to the session factory's settings: 我在会话工厂的设置中添加了以下内容:
properties.put(AvailableSettings.FLUSH_BEFORE_COMPLETION, true);
and removed custom flush done in Synchronization
. 并删除了在
Synchronization
完成的自定义刷新。 This solves the issue when doing rollback
but now, doing commit
fails. 这解决了
rollback
时的问题,但是现在, commit
失败。 When doing an explicit getcurrentSession().flush()
both commit and rollback work fine. 当执行显式的
getcurrentSession().flush()
,提交和回滚都可以正常工作。
The issue lied in our implementation of TransactionManager: Synchronization listeners where run within a context where current transaction had been removed, hence implicit flush()
was creating its own transaction when invoked automatically as part of transaction completion procedure. 问题在于我们在TransactionManager的实现中存在的问题:同步侦听器在已删除当前事务的上下文中运行,因此在作为事务完成过程的一部分自动调用时,隐式
flush()
正在创建自己的事务。 When flush was invoked explicitly things were working fine of course because transactional context was still in place. 当显式调用flush时,由于事务上下文仍然存在,因此当然一切正常。
I fixed our TM to ensure transaction context is cleared after tx is committed/rollbacked and things are now working fine. 我修复了TM,以确保在提交/回滚tx 之后清除事务上下文,并且现在一切正常。
I also removed our custom Synchronization which is a (naive) duplicate of what Hibernate provides natively and which is available when one sets the FLUSH_BEFORE_COMPLETION
flag. 我还删除了自定义同步,它是Hibernate本地提供的(原始)副本,当设置
FLUSH_BEFORE_COMPLETION
标志时可以使用。 When in a rollback, the session is actually emptied instead of flushed meaning no interaction with the DB happens, which saves bandwidth and resource from the DBMS. 在回滚时,实际上会清空会话,而不是清空会话,这意味着不会与数据库进行任何交互,从而节省了DBMS的带宽和资源。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.