[英]Spring 4 + JPA (Hibernate 4) + JTA transaction manager doesn't flush automatically
[英]Hibernate session flush on rollback fails with custom JTA Transaction Manager
我在自定义应用程序服务器中使用Hibernate 4.2.2.Final,它提供了JTA Transaction Manager的自定义实现来管理事务上下文。 我们使用DAO模式从使用中抽象化管理休眠会话的细节,并在需要时透明地注入事务上下文。
这是我们配置会话工厂的方式:
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());
}
在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);
}
}
但是,以下测试失败,测试结束时的断言期望表为空,但不是:
@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());
}
我可以看到调用了事务侦听器并刷新了会话,但是看上去刷新发生得太晚了……
使用当前会话的显式刷新修复测试时,它通过:
@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());
}
我将问题归结为调试级别的日志,无法理解有什么不同:在日志中,刷新似乎在回滚事务之前已正确完成。
我想念什么? 我找不到合适的示例来实现这种情况(我可能没有正确搜索...),我想我会遵循Hibernate文档中记录的内容。
我在会话工厂的设置中添加了以下内容:
properties.put(AvailableSettings.FLUSH_BEFORE_COMPLETION, true);
并删除了在Synchronization
完成的自定义刷新。 这解决了rollback
时的问题,但是现在, commit
失败。 当执行显式的getcurrentSession().flush()
,提交和回滚都可以正常工作。
问题在于我们在TransactionManager的实现中存在的问题:同步侦听器在已删除当前事务的上下文中运行,因此在作为事务完成过程的一部分自动调用时,隐式flush()
正在创建自己的事务。 当显式调用flush时,由于事务上下文仍然存在,因此当然一切正常。
我修复了TM,以确保在提交/回滚tx 之后清除事务上下文,并且现在一切正常。
我还删除了自定义同步,它是Hibernate本地提供的(原始)副本,当设置FLUSH_BEFORE_COMPLETION
标志时可以使用。 在回滚时,实际上会清空会话,而不是清空会话,这意味着不会与数据库进行任何交互,从而节省了DBMS的带宽和资源。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.