2013-09-26 156 views
1

當前位於具有本地和遠程EJB的MDB(Singleton和Stateless)的JavaEE應用程序服務器中,我正在使用JDBC-Transactions for Hibernate Core。在拋出異常時回滾事務並關閉連接

管理自己我所有的開啓和關閉,提交休眠會話和事務可能導致連接泄漏和未合併的事務。
特別是在編程錯誤導致自定義或未經檢查的異常未捕獲並拋出到遠程客戶端的情況下。

什麼是最簡單或最好的方式來確保我的休眠會話被關閉,事務回滾以防錯誤發生?

使用容器管理的事務(CMT)還是可以關閉在任何EJB方法返回時調用的攔截器內的會話?

一個簡單的方法是將會話範圍的用法包裝在try-catch塊中,並捕獲任何類型的異常,但採用較少代碼的一般方法會受到青睞。

編輯:遠程EJB實例

  • 我低級別的Hibernate DAO不關閉連接和拋出的異常回滾事務。問題是業務邏輯之間的DAO訪問的情況下,連接仍處於打開*

    public void doSomething(Foo foo) throws Exception 
    { 
        // open session and transaction 
        Session session = DAO.openSession(); 
    
        // retrieve data 
        Bar bar = DAO.get(session, ...) 
    
        // call other methods which throws an exception resulting in open connection 
        doOtherStuff(foo, bar) 
    
        DAO.save(session, foo); 
    
        // commit transaction 
        DAO.closeAndCommitSession(session); 
    } 
    

現在我使用的是大的try-catch-最後:

public void doSomething(Foo foo) throws Exception 
    { 
     // open session and transaction 
     Session session = DAO.openSession(); 
     try 
     { 
      // retrieve data 
      Bar bar = DAO.get(session, ...) 

      // call other methods which throws an exception resulting in open connection 
      doOtherStuff(foo, bar) 

      DAO.save(session, foo); 
     } 
     catch (final Exception e) 
     { 
      DAO.rollBackTransaction(session); 
      throw e; 
     } 
     finally 
     { 
      DAO.closeAndCommitSession(session); 
     } 
    } 

回答

2

一般來說這個問題正在做資源管理正確和在簡單的方式。這總是需要兩個要素:一個簡單的API和紀律,以在任何地方使用這個API。

可能不適合使用Hibernate,但只是爲了顯示的總體思路你的使用情況的具體解決方案:使用貸款模式

public interface ConnectionRunnable { 
    public void run(Connection conn) throws SQLException; 
} 

該代碼會更優雅,如果你使用的Groovy或Scala(封閉)。在這種情況下,這個接口是不需要的。

反正到處都需要一個連接,使用這樣的:

public static void withConnection(final String dataSourceName, 
      ConnectionRunnable runnable) throws SQLException, NamingException { 
     final InitialContext ic = new InitialContext(); 
     final DataSource ds = (DataSource) ic.lookup(dataSourceName); 

     final Connection conn = ds.getConnection(); 
     try { 
      runnable.run(conn); 
     } finally { 
      conn.close(); 
     } 
    } 

在你的EJB中使用這樣的:

ConnectionUtils.withConnection("java:/jdbc/sandbox", new ConnectionRunnable() { 
    public void run(Connection conn) throws SQLException { 
     // Do something with the connection 
    } 
}); 
  • 使用的貸款模式,你根本就忘記關閉連接
  • 如果是SQLExceptionNamingException您可以集中決定如何正確處理:使用BMT手動提交或回滾。使用setRollbackOnly或拋出SystemException觸發容器(使用CMT)等。如果沒有特殊原因,請使用CMT。
  • 即使您稍後更改錯誤處理,它也只是一個代碼片段。

更新:使用ConnectionRunnable接口的開銷。

ConnectionRunnable僅僅是一種「鍵入開銷」:Groovy和Scala的實現以相同的方式關閉,運行時開銷可以忽略不計。 Java 8將以相同的方式實現閉包。

在這種情況下,提交或回滾數據庫事務的成本無論如何都會高得多。

從Java /過程/慣用的角度來看,它看起來很奇怪,關於異常處理的代碼重用的想法也可能看起來很奇怪。

使用Scala的它看起來像這樣在使用場所(直譯):

ConnectionUtils.withConnection("java:/jdbc/sandbox") { 
    conn => 
    // Do something with the connection 
} 

但引擎蓋下它使用類似於在JVM上的一個匿名類爲好。

+0

這將需要每個方法的'ConnectionRunnable',這似乎是非常大的開銷。關閉連接是最低優先級,因爲這只是一個編程錯誤。問題更多的是未捕獲的異常,當連接仍處於打開狀態並且事務未回滾時,將拋出異常。 – djmj

+1

@djmj:Beryllium提出的解決方案看起來簡潔而優雅,即使它需要每個連接的匿名內部類。這裏的美妙之處在於,它允許您以更確定的方式管理資源並處理異常。我錯過了什麼嗎? – scottb

+0

@scottb不錯的總結!我只想補充說,在使用站點上需要更少的代碼行:嵌套的異常處理在每個使用站點都會使代碼混亂。 – Beryllium

相關問題