简体   繁体   English

自定义JTA事务管理器在回滚时休眠会话刷新失败

[英]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文档中记录的内容。

Update: 2014-05-19 更新时间:2014-05-19

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.

相关问题 Spring 4 + JPA(Hibernate 4)+ JTA事务管理器不会自动刷新 - Spring 4 + JPA (Hibernate 4) + JTA transaction manager doesn't flush automatically Hibernate事务回滚是否删除“session.flush()”ed实体? - Does a Hibernate transaction rollback delete “session.flush()”ed entities? 将休眠事务集成到JTA事务管理器中 - Integration of hibernate transaction in jta transaction manager 使用Hibernate 4中的事务功能是否需要JTA管理器 - Is JTA manager necessary to use transaction features in hibernate 4 Hibernate JTA多用户会话事务映射 - Hibernate JTA multiuser session-transaction mapping 手动调用flush()时,无法将Glassfish 4上的Hibernate 4 / Hibernate 5与JTA事务一起加入JDBC事务 - Can't join JDBC transaction with JTA transaction with Hibernate 4/Hibernate 5 on Glassfish 4 when flush() is manually called Spring Data,Hibernate 4.1.1和Spring JTA事务管理器 - Spring Data, Hibernate 4.1.1 and Spring JTA Transaction Manager JTA 和 Hibernate 事务管理 - JTA and Hibernate Transaction management hibernate.transaction.manager_lookup_class VS hibernate.transaction.jta.platform - hibernate.transaction.manager_lookup_class VS hibernate.transaction.jta.platform 如何修复 JBOSS EAP 7.2 中 JTA 事务的 hibernate.transaction.flush_before_completion 问题? - How to fix hibernate.transaction.flush_before_completion issue with JTA transactions in JBOSS EAP 7.2?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM