2009-06-11 58 views
3

我正在寫一些簡單的舊的主要是JDBC模式的servlet。我意識到我有幾個想要共享單個事務的對象,並且我想強制執行一個HTTP事務=一個數據庫事務。ThreadLocal + java.sql.Connection + servlet filter = 2009?

我想我可以通過在一個ThreadLocal變量來回傳送連接,然後有一個Servlet過濾器處理產生的操作/提交上述連接的/回滾。

有一個現有的框架,這是否說我並不瞭解,或者這是一個合理的晚00的方式來做事?

+0

爲什麼你不能將Connection作爲你調用的方法的參數? – Eduardo 2011-08-26 05:04:38

回答

4

Spring事務管理不正是你的描述,這可能是多一點乍一看空前的,但你也將需要(爲簡單的情況下)是:

org.springframework.jdbc.datasource.DataSourceTransactionManager org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy org.springframework.transaction.support.TransactionTemplate

連接你現有的DataSource並將其包裝在TransctionAwareDataSourceProxy中,然後用包裝的數據源創建一個DataSourceTransactionManager,並將它們保存在你的ServletContext中。然後爲每個事務創建一個TransactionTemplate傳入事務管理器並調用execute(TransactionCallback)方法來運行你的代碼。例如:

new TransactionTemplate(transactionManager).execute(new TransactionCallback(){ 
    public void doInTransaction(TransactionStatus ts){ 
     // run your code here...use the dataSource to get a connection and run stuff 
     Connection c = dataSourceProxy.getConnection(); 
     // to rollback ... throw a RuntimeException out of this method or call 
     st.setRollbackOnly(); 
    } 
}); 

的連接將被本地所以只要你總是連接形成同一數據源即包裹一個綁定到一個線程,你會得到在同一事務在同一個連接。

注意,這是一個最簡單的Spring事務設置...不nessarly最好的或推薦的一個,對於具有看看Spring參考文檔的查看或行動閱讀的春天。

...所以我想作爲一個直接的答案,它是在做一件合理的事,這就是Spring框架已經做了很長時間。

+0

我同意,如果你想使用普通的JDBC,但是不要重蹈覆轍,SpringFrameowrk JDBC支持可以很容易地嵌入,而且JDBC API非常小,你將有益於重用已經編寫和測試過的東西。 – 2009-06-12 02:38:32

+0

我將不得不檢查它,我試圖避免在這個應用程序中的大型框架(因此使用普通的servlet),但如果我可以使用Spring而不用全下,它可能是一個解決方案。 – sehugg 2009-06-12 14:23:49

0

它通常是更好的通過傳遞對象,具有「從上面參數設置」中,sleazing與ThreadLocal。在ServletFilter的情況下,ServletRequest的屬性將是顯而易見的地方。非servlet相關代碼的接口可以將Connection提取爲有意義的上下文。

0

有一個過濾器管理事務是一個很好的方法來滾動你自己的事務管理。

Java EE規範提供了事務管理,而像彈簧替代框架提供類似的支持(儘管這不是一種認可;春天並不一定做好這一點)。

但是,使用ThreadLocal會產生問題。例如,不能保證在整個請求中使用單個線程,任何人都可以通過全局變量訪問Connection,並且如果依賴於要設置的某個全局狀態,則測試可能變得更加困難。我會考慮使用依賴注入容器來明確地將Connection傳遞給需要它的對象。

1

大多數APPSERVER今天支持JTA(Java事務API):橫跨在多個打開/關閉JDBC連接的事務。它爲你提供「threadLocal」的東西,它符合J2EE標準。 您可以使用它像這樣在你的過濾器:

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 
    throws IOException, ServletException { 
    UserTransaction transaction = null; 
    try { 
     transaction = (UserTransaction)new InitialContext().lookup("java:comp/UserTransaction"); 
     transaction.begin(); 
     chain.doFilter(request, response); 
     transaction.commit(); 
    } catch (final Exception errorInServlet) { 
     try { 
      transaction.rollback(); 
     } catch (final Exception rollbackFailed) { 
      log("No ! Transaction failed !",rollbackFailed); 
     } 
     throw new ServletException(errorInServlet); 
    } 
} 

在應用服務器,聲明具有JNDI名稱的數據源,並用它在你的代碼來獲取連接(不要讓cx.commit() ,cx.rollback()或cx.setAutocommit()的東西,它會干擾JTA)。您可以打開和關閉連接多次在同一個HTTP事務,JTA會照顧它:

public void doingDatabaseStuff() throws Exception { 
    DataSource datasource = (DataSource)new InitialContext().lookup("/path/to/datasource"); 
    Connection connection = datasource.getConnection(); 
    try { 
     // doing stuff 
    } finally { 
     connection.close(); 
    } 
} 
0

如果你不能依靠一個「真正的」應用程序服務器上,並且要避免不,所謂Spring的輕量級,使用過濾器提供連接,將其保留在線程上並在請求結束時關閉它確實是一種實用且合理的解決方案。

您需要一些(本質上是靜態的)訪問器類,它允許get()連接和setRollbackOnly()。

在請求結束時,從過濾器的角度來看,確保捕獲異常(應該記錄並設置爲僅回滾)和提交/回滾,並相應地關閉事務。

在大多數應用程序和Web容器中(和JTA通常會做出類似的假設),一個請求將由一個線程處理,並且在請求期間將一個數據庫連接與線程關聯以在層之間重用是恰當的事情去做。