簡體   English   中英

自定義JTA事務管理器在回滾時休眠會話刷新失敗

[英]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文檔中記錄的內容。

更新時間:2014-05-19

我在會話工廠的設置中添加了以下內容:

                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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM