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

发布于 2024-07-24 23:06:58 字数 238 浏览 5 评论 0原文

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

我想我可以通过在 ThreadLocal 变量中传递 Connection,然后让 servlet 过滤器处理所述 Connection 的创建/提交/回滚来完成此操作。

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

I am writing some servlets with plain old mostly-JDBC patterns. I realized that I have several objects that would like to share a single transaction, and I'd like to enforce that one HTTP transaction = one database transaction.

I think I can do this via passing a Connection around in a ThreadLocal variable, and then having a servlet filter handling the creation/commit/rollback of said Connection.

Is there an existing framework that does this that I'm not privy to, or is this a reasonable late-00's way to do things?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

带上头具痛哭 2024-07-31 23:06:58

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 事务设置...不一定是最好的或推荐的,因为请查看 spring 参考文档或阅读 spring 的实际操作。

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

Spring transaction management does exactly what you describe, it might be a little over whelming at first glance but all you will be needing (for the simplest case) is:

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

Wire up your existing DataSource and wrap it in the TransctionAwareDataSourceProxy then create a DataSourceTransactionManager with the wrapped data source, keep these in your ServletContext. Then for each transaction create a TransactionTemplate passing in the transaction manager and call the execute(TransactionCallback) method to run your code. eg:

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();
    }
});

The connection will be bound to a thread local so as long as you always get the connection form the same datasource i.e. the wrapped one, you'll get the same connection in the same transaction.

Note this is the simplest possible spring transaction setup ... not nessarly the best or recommended one, for that have a look at the spring reference doc's or read spring in action.

... so I guess as a direct answer, yes it is a reasonable thing to be doing, it's what the spring framework has been doing for a long time.

雨落□心尘 2024-07-31 23:06:58

当今大多数应用程序服务器都支持 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();
    }
}

Most appServer todays support JTA (Java Transaction Api): A transaction that spans over multiple open/close jdbc connections. It does the "threadLocal" stuff for you and it's J2EE compliant.
You use it like this in your filter:

    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);
    }
}

On the app-server, declare a Datasource with a jndi name, and use it in your code to retrieve a connection (do NOT make cx.commit(), cx.rollback() or cx.setAutocommit() stuff, it will interfere with JTA). You can open and close your connection several times in the same HTTP transaction, JTA will take care of it:

public void doingDatabaseStuff() throws Exception {
    DataSource datasource = (DataSource)new InitialContext().lookup("/path/to/datasource");
    Connection connection = datasource.getConnection();
    try {
        // doing stuff
    } finally {
        connection.close();
    }
}
我的黑色迷你裙 2024-07-31 23:06:58

通常最好使用“来自上方的参数化”传递对象,并使用 ThreadLocal 进行偷窃。 对于 ServletFilter 来说,ServletRequest 的属性将是一个明显的位置。 非 Servlet 相关代码的接口可以将Connection 提取到有意义的上下文。

It is generally better to pass object with "Parameterisation from Above", the sleazing through with ThreadLocal. In the case of ServletFilter, an attribute of the ServletRequest would be an obvious place. The interface to non-servlet dependent code can extract the Connection to meaningful context.

风尘浪孓 2024-07-31 23:06:58

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

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

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

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

If you cannot rely on a "real" app server and you want to avoid the not-so-lightweightness of Spring, using a filter to provide a connection, keep it on the thread and close it at the end of the request is indeed a practical and reasonable solution.

You would need some (essentially static) accessor class that allows to get() a connection and a setRollbackOnly().

Upon end of the request, from the filter's perspective, make sure to catch exceptions (upon which you should log and set to rollback only) and commit/rollback, close the transaction accordingly.

In most applications and web containers (and JTA usually makes similar assumptions) a request will be processed by exactly one thread and associating the one database connection with the thread for re-use between layers during the request is just the right thing to do.

甜是你 2024-07-31 23:06:58

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

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

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

Having a filter manage the transaction is a good approach to rolling your own transaction management.

The Java EE specification provides for transaction management, and alternative frameworks like Spring provide similar support (though that's not an endorsement; Spring doesn't necessarily do this well).

However, use of a ThreadLocal can create problems. For example, there are no guarantees that a single thread is used throughout a request, anything can access the Connection through the global variable, and testing can become more difficult if you are depending on some global state to be set up. I'd consider using a dependency injection container to explicitly pass a Connection to objects that need one.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文