ThreadLocal + java.sql.Connection + servlet 过滤器 = 2009?

Posted

技术标签:

【中文标题】ThreadLocal + java.sql.Connection + servlet 过滤器 = 2009?【英文标题】:ThreadLocal + java.sql.Connection + servlet filter = 2009? 【发布时间】:2010-11-02 05:10:48 【问题描述】:

我正在编写一些 servlet,这些 servlet 具有简单的旧的主要是 JDBC 模式。我意识到我有几个对象想要共享一个事务,并且我想强制执行一个 HTTP 事务 = 一个数据库事务。

我想我可以通过在 ThreadLocal 变量中传递一个 Connection 来做到这一点,然后让一个 servlet 过滤器处理所述 Connection 的创建/提交/回滚。

是否有一个我不知道的现有框架可以做到这一点,或者这是 00 后合理的做事方式?

【问题讨论】:

为什么不能将 Connection 作为所调用方法的参数传递? 【参考方案1】:

通常最好使用“从上方进行参数化”传递对象,而使用ThreadLocal 则更好。在ServletFilter 的情况下,ServletRequest 的属性将是一个明显的位置。非 servlet 依赖代码的接口可以将Connection 提取到有意义的上下文中。

【讨论】:

【参考方案2】:

使用过滤器管理事务是滚动您自己的事务管理的好方法。

Java EE 规范提供了事务管理,而 Spring 等替代框架提供了类似的支持(尽管这不是一种认可;Spring 不一定能很好地做到这一点)。

但是,使用ThreadLocal 会产生问题。例如,不能保证在整个请求中使用单个线程,任何东西都可以通过全局变量访问Connection,如果您依赖于要设置的某些全局状态,测试可能会变得更加困难。我会考虑使用依赖注入容器将Connection 显式传递给需要的对象。

【讨论】:

【参考方案3】:

Spring 事务管理完全按照您的描述进行,乍一看可能有点令人印象深刻,但您所需要的(对于最简单的情况)是:

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

连接您现有的数据源并将其包装在 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 事务设置...不是最好或推荐的设置,请查看 spring 参考文档或阅读 spring in action。

...所以我想作为一个直接的答案,是的这是一个合理的事情,这是 Spring 框架长期以来一直在做的事情。

【讨论】:

我同意,如果你想要普通的 JDBC,那很好,但不要重新发明***,SpringFrameowrk JDBC 支持可以很容易嵌入,JDBC API 非常小,你将受益于重用已经编写的内容并经过测试。 我将不得不检查一下,我试图避免在这个应用程序中使用大型框架(因此使用普通的 servlet)但是如果我可以使用 Spring 而无需全力以赴,它可能是一个解决方案。 我有点猜到你在哪里采用最小的方法。选择模块化罐子 spring-transaction.jar 和 spring-core 等,你应该能够逃脱而不必包含 uber 2mb spring.jar 我只需要 5 个 .jar,然后我转换为 JdbcTemplate 样式。反正我讨厌直接的 JDBC 编程;) 好结果。如果您使用的是 JdbcTemplate 东西,则不需要使用 TransactionAwareDataSourceProxy 东西,因为 JdbcTemplate 是事务感知的,并且会为您整理这些东西。【参考方案4】:

当今的大多数 appServer 都支持 JTA(Java Transaction 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();
    

【讨论】:

JTA 看起来很方便,不知道JTA 是否与Tomcat/Jetty 集成得很好?我试图使应用程序的这一部分尽可能精简(即只是 servlet、最小的依赖项等) 更多关于JTA:***.com/questions/840694/… 我不得不承认我只在 $$$ appServers 上使用了 JTA。 Tomcat 和 Jetty 似乎并没有自己提供 JTA 实现。 Jboss 和 JOTM 提供独立的 JTA impl,但它们带有 ejb 支持或看起来很复杂。您在 BTM (docs.codehaus.org/display/BTM/Home) 上给出的链接似乎满足您的所有需求(活跃的社区、免费、轻量级、有据可查的集成和使用)。老实说,我不知道它是否会起作用,但听起来很有希望。 顺便说一句,看看教程(docs.codehaus.org/display/BTM/JtaBestPractices),非常简洁明了,还有一点优化(docs.codehaus.org/display/BTM/LastResourceCommit13) 借助这两个链接(如下)和最后两个链接:与 Tomcat 集成(docs.codehaus.org/display/BTM/Tomcat13),与 Jetty 集成(docs.codehaus.org/display/BTM/Jetty13),我想你将能够快速构建概念验证。【参考方案5】:

如果你不能依赖一个“真正的”应用服务器并且你想避免 Spring 不那么轻量级,使用过滤器来提供连接,保持它在线程上并在请求结束时关闭它确实是一个实用且合理的解决方案。

您将需要一些(基本上是静态的)访问器类来允许 get() 连接和 setRollbackOnly()。

在请求结束时,从过滤器的角度来看,确保捕获异常(您应该记录并设置为仅回滚)并提交/回滚,并相应地关闭事务。

在大多数应用程序和 Web 容器中(JTA 通常做出类似的假设),一个请求将由一个线程处理,并且将一个数据库连接与线程相关联以便在请求期间在层之间重用是正确的做法做。

【讨论】:

以上是关于ThreadLocal + java.sql.Connection + servlet 过滤器 = 2009?的主要内容,如果未能解决你的问题,请参考以下文章

ThreadLocal(一)设计ThreadLocal的目的

ThreadLocal 的原理讲述 + 基于ThreadLocal实现MVC中的M层的事务控制

对ThreadLocal的一些理解

ThreadLocal源码解析-Java8

ThreadLocal

手撕面试题ThreadLocal!!!