ThreadLocal + java.sql.Connection + servlet 过滤器 = 2009?
我正在使用普通的旧式主要 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
Spring 事务管理正是按照你所描述的那样做的,乍一看可能有点令人难以接受,但你会明白的需要(最简单的情况)是:
org.springframework.jdbc.datasource.DataSourceTransactionManager
org.springframework.jdbc。 datasource.TransactionAwareDataSourceProxy
org.springframework.transaction。 support.TransactionTemplate
连接现有的 DataSource 并将其包装在 TransctionAwareDataSourceProxy 中,然后使用包装的数据源创建一个 DataSourceTransactionManager,将它们保留在 ServletContext 中。 然后为每个事务创建一个传入事务管理器的 TransactionTemplate 并调用execute(TransactionCallback) 方法来运行代码。 例如:
连接将绑定到本地线程,因此只要您始终从同一数据源(即包装的数据源)获取连接,您就会在同一事务中获得相同的连接。
请注意,这是最简单的 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:
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.
当今大多数应用程序服务器都支持 JTA(Java 事务 Api):跨越多个打开/关闭 jdbc 连接的事务。 它为您完成“threadLocal”的工作,并且符合 J2EE。
您可以在过滤器中像这样使用它:
在应用程序服务器上,声明具有 jndi 名称的数据源,并在代码中使用它来检索连接(不要使 cx.commit()、cx.rollback() 或 cx .setAutocommit() 之类的东西,它会干扰 JTA)。 您可以在同一个 HTTP 事务中多次打开和关闭连接,JTA 会处理它:
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:
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:
通常最好使用“来自上方的参数化”传递对象,并使用 ThreadLocal 进行偷窃。 对于
ServletFilter
来说,ServletRequest
的属性将是一个明显的位置。 非 Servlet 相关代码的接口可以将Connection
提取到有意义的上下文。It is generally better to pass object with "Parameterisation from Above", the sleazing through with
ThreadLocal
. In the case ofServletFilter
, an attribute of theServletRequest
would be an obvious place. The interface to non-servlet dependent code can extract theConnection
to meaningful context.如果你不能依赖一个“真正的”应用服务器,并且你想避免 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.
使用过滤器管理事务是滚动您自己的事务管理的好方法。
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 theConnection
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 aConnection
to objects that need one.