【问题标题】:Hibernate session flush on rollback fails with custom JTA Transaction Manager使用自定义 JTA 事务管理器回滚时的休眠会话刷新失败
【发布时间】:2014-07-05 02:13:46
【问题描述】:

我在自定义应用程序服务器中使用 Hibernate 4.2.2.Final,提供 JTA 事务管理器的自定义实现来管理事务上下文。我们使用 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 文档中记录的内容。

更新:2014-05-19

我在会话工厂的设置中添加了以下内容:

                properties.put(AvailableSettings.FLUSH_BEFORE_COMPLETION, true);

并删除了在Synchronization 中完成的自定义刷新。这解决了执行 rollback 时的问题,但现在执行 commit 失败。当执行明确的getcurrentSession().flush() 时,提交和回滚都可以正常工作。

【问题讨论】:

    标签: hibernate session transactions jta


    【解决方案1】:

    问题在于我们的 TransactionManager 实现:同步侦听器在当前事务已被删除的上下文中运行,因此隐式 flush() 在作为事务完成过程的一部分自动调用时创建了自己的事务。当显式调用刷新时,当然一切正常,因为事务上下文仍然存在。

    我修复了我们的 TM 以确保在提交/回滚 tx 之后 清除事务上下文,并且现在一切正常。

    我还删除了我们的自定义同步,它是 Hibernate 原生提供的(简单)副本,当设置 FLUSH_BEFORE_COMPLETION 标志时可用。在回滚时,会话实际上是清空而不是刷新,这意味着不会发生与 DB 的交互,这节省了 DBMS 的带宽和资源。

    【讨论】:

      猜你喜欢
      • 2011-12-10
      • 2013-08-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-06-18
      • 1970-01-01
      • 2012-02-03
      • 1970-01-01
      相关资源
      最近更新 更多