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

ThreadLocal+;java.sql.Connection+;servlet过滤器=2009?,java,servlets,jdbc,servlet-filters,thread-local,Java,Servlets,Jdbc,Servlet Filters,Thread Local,我正在用简单的旧模式(主要是JDBC模式)编写一些servlet。我意识到我有几个对象想要共享一个事务,我想强制执行一个HTTP事务=一个数据库事务 我想我可以通过在ThreadLocal变量中传递一个连接来实现这一点,然后让servlet过滤器处理所述连接的创建/提交/回滚 是否有一个现有的框架可以做到这一点,而我对此并不了解,或者这是一种合理的晚00的做事方式?通常最好使用“从上方参数化”来传递对象,即使用线程本地进行搜索。在ServletFilter的情况下,ServletRequest的

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

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


是否有一个现有的框架可以做到这一点,而我对此并不了解,或者这是一种合理的晚00的做事方式?

通常最好使用“从上方参数化”来传递对象,即使用
线程本地
进行搜索。在
ServletFilter
的情况下,
ServletRequest
的属性将是一个明显的位置。与非servlet相关的代码的接口可以将
连接提取到有意义的上下文。

使用过滤器管理事务是一种很好的方法来滚动您自己的事务管理

JavaEE规范为事务管理提供了支持,Spring等替代框架也提供了类似的支持(尽管这不是认可;Spring不一定做得很好)

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

事务管理完全按照你所描述的那样做,它可能在乍一看中有点过分,但是你所需要的(最简单的情况)是:

连接现有数据源并将其包装在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的实际操作


。。。因此,我想作为一个直接的回答,是的这是一个合理的做法,spring框架已经做了很长时间。

现在大多数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();
    }
}
如果你不能依赖一个“真正的”应用服务器,并且你想避免Spring的轻量级,那么使用一个过滤器来提供一个连接,将它保持在线程上,并在请求结束时关闭它确实是一个实用而合理的解决方案

您需要一些(本质上是静态的)访问器类,它允许获取()连接和setRollbackOnly()

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


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

我同意,如果您想要普通JDBC,这很好,但不要重新发明轮子,SpringFrameowrk JDBC支持可以非常容易地嵌入,而且JDBC API非常小,您将受益于重用已经编写和测试的内容。我将不得不检查一下,我试图避免在这个应用程序中使用大型ish框架(因此使用普通servlet)但是,如果我可以使用Spring而不必全力以赴的话,它可能是一个解决方案。我有点想你应该选择一种最简单的方法。选择模块化jars spring-transaction.jar和spring-core等,您应该可以不必包含uber 2mb spring.jar,我只需要5.jars,就可以转换为JdbcTemplate风格。我讨厌直接的JDBC编程;)好结果。如果您使用的是JdbcTemplate,那么就不需要使用TransactionWaredataSourceProxy,因为JdbcTemplate是事务感知的,可以为您整理这些内容。JTA看起来很方便,我想知道JTA是否与Tomcat/Jetty很好地集成?我正试图使应用程序的这一部分尽可能精简(即,仅servlet、最小依赖项等),更多关于JTA的信息:我必须承认,我只在$$$AppServer上使用了JTA。看来Tomcat和Jetty本身并没有提供JTA实现。Jboss和JOTM提供独立的JTA impl,但它们附带ejb支持,或者看起来很复杂。您在BTM()上给出的链接似乎满足了您的所有需求(活动社区、免费、轻量级、文档完整的集成和使用)。老实说,我不知道它是否会起作用,但听起来很有希望。顺便说一句,看看教程(),它非常简洁明了,还有这一点优化()借助于以下两个链接和最后两个链接:与Tomcat的集成(),我