2009-06-11 94 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通常会做出类似的假设),一个请求将由一个线程处理,并且在请求期间将一个数据库连接与线程关联以在层之间重用是恰当的事情去做。