2017-02-21 29 views
0

從不包含任何髒對象的實體管理器提交事務時會發生什麼?是否沒有COMMIT命令發送到數據庫?JPA提交由解包連接完成的更改

我有一些測試用例失敗,然後沒有合理的原因。經過一番調查後,我現在有一個理論,我想在這裏證實。

我有一個小夾具框架來準備每個測試數據庫的數據。燈具用這樣的方法將對象存儲到使用JPA(休眠)的DB:

public <R> R doInTransaction(final Function<EntityManager, R> whatToDo) { 
    final EntityManager em = emf.createEntityManager(); 
    final R result; 
    try { 
     try { 
     em.getTransaction().begin(); 
     result = whatToDo.apply(em); 
     em.getTransaction().commit(); 
     } finally { 
     if (em.getTransaction().isActive()) { 
      em.getTransaction().rollback(); 
     } 
     } 
    } finally { 
     em.close(); 
    } 
    return result; 
    } 

因此,夾具調用此方法傳遞whatToDo功能,而對象仍和方法周圍的傳遞函數包裝了一個交易。我失敗的測試用例正在使用依賴於使用存儲過程的遺留代碼的fixture,並通過JDBC直接存儲對象。即而是採用em.persist(),我用下面的傳遞函數來調用存儲過程:

em.unwrap(Session.class).doWork(connection -> { 
    // stored procedures are called here directly over JDBC 
}); 

所以,我的理論是,JPA在這種情況下也不會立即提交,因爲有由管理沒有JPA髒對象EntityManager的。因此,實際的提交只會在晚些時候發生。即在我的測試聲明和測試失敗後。它會是嗎?

當「解開」連接到EntityManager之外時,Hibernate的事務行爲是什麼?

我已經在em.getTransaction().commit()之前添加了一個em.flush(),它似乎有所幫助,但我仍然不是百分之百相信這可以解決問題。有人可以證實嗎?

+0

混合和匹配持久處理程序時,您必須小心。 Hibernate維護一個什麼是髒的緩存,什麼不是。如果你在hibernate以外的地方更新東西,那麼hibernate有可能會與db不同步並刪除更新,因爲它認爲它的實體的緩存版本仍然有效。過去幾年我經常遇到類似這樣的問題,如果我沒有記錯的話,我們在hibernate中禁用緩存以防止這種行爲。 – Stephan

+0

我不喜歡在Hibernate中進入緩存的神祕世界...... :)如果'em.flush()'在這裏完成這項工作,這對我來說已經足夠了。 – Alex

+0

如果你在測試環境中,可以隨時嘗試em.flush()並查看。就緩存設置而言,如果您不希望有太多併發用戶,您應該能夠在hibernate中禁用緩存,並讓數據庫中的緩存處理大部分工作。這是另一回事,數據庫的默認緩存會干擾休眠的默認緩存。如果您繼續遇到問題,我強烈建議關閉休眠緩存。 – Stephan

回答

0

無論解開連接,行爲都是相同的。如果您不使用JTA,則替代方案是由JDBC提供的基礎事務,即本地事務。 (或者您可以實現自己的管理事務提供者)

當您打開連接並直接處理JDBC時,仍然會獲得與此會話/實體管理器最初獲取的連接相同的連接。所以這是一樣的效果。

+0

更正,解開連接並直接處理JDBC會給我相同的連接,這就是爲什麼我希望'em.getTransaction()。commit()'也會提交直接在JDBC上完成的更改。但是,如果沒有髒物體,這顯然不一定是這種情況,對吧?我的理論是有效的嗎? em.flush()會強制執行嗎?我更喜歡不直接從JDBC提交,所以doInTransaction()內部所做的任何事情都是從數據庫角度來看的單一事務。 – Alex

+0

如果對JPA實體進行了任何更新,則將其標記爲髒。在Hibernate中,對實體的更改將轉換爲事件,只有在刷新時,事件纔會被轉換爲SQL並傳遞給JDBC。如果沒有更改(沒有髒實體),則管道中將不會有任何SQL,因此提交或不提交將具有相同的效果,即無操作。 – raminr