java 如何获得 Spring 事务内部的连接?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/11779469/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do I get the connection inside of a Spring transaction?
提问by Aaron Digulla
Imagine this code:
想象一下这段代码:
foo() {
Connection conn = ...;
}
foo()
has been called from a method that has the annotation @Transactional
. How do I get the current JDBC connection? Note that foo()
is in a bean (so it can have @Autowired
fields) but foo()
can't have parameters (so I can't pass the connection in from somewhere).
foo()
已从具有注释的方法调用@Transactional
。如何获取当前的 JDBC 连接?请注意,foo()
它在一个 bean 中(所以它可以有@Autowired
字段)但foo()
不能有参数(所以我不能从某个地方传入连接)。
[EDIT]I'm using jOOQ which needs either a data source or a connection. My problem: I don't know which transaction manager is configured. It could be anything; Java EE, DataSource based, something which gets the data source via JNDI. My code isn't an application, it's a library. I need to swallow what others put on my plate. Along the same lines, I can't request a Hibernate session factory because the application using me might not use Hibernate.
[编辑]我正在使用需要数据源或连接的 jOOQ。我的问题:我不知道配置了哪个事务管理器。它可以是任何东西;Java EE,基于数据源,通过 JNDI 获取数据源的东西。我的代码不是应用程序,而是库。我需要吞下别人放在我盘子里的东西。同样,我无法请求 Hibernate 会话工厂,因为使用我的应用程序可能不使用 Hibernate。
But I know that other code, like the Spring Hibernate integration, somehow can get the current connection from the transaction manager. I mean, Hibernate doesn't support Spring's transaction manager, so the glue code must adapt the Spring API to what Hibernate expects. I need to do the same thing but I couldn't figure out how it works.
但我知道其他代码,如 Spring Hibernate 集成,可以以某种方式从事务管理器获取当前连接。我的意思是,Hibernate 不支持 Spring 的事务管理器,因此粘合代码必须使 Spring API 适应 Hibernate 的要求。我需要做同样的事情,但我无法弄清楚它是如何工作的。
[EDIT2]I know that there is an active transaction (i.e. Spring has a Connection instance somewhere or at least a transaction manager which can create one) but my method isn't @Transactional. I need to call a constructor which takes java.sql.Connection
as parameter. What should I do?
[EDIT2]我知道有一个活动事务(即 Spring 在某处有一个 Connection 实例,或者至少有一个可以创建一个的事务管理器),但我的方法不是 @Transactional。我需要调用一个java.sql.Connection
作为参数的构造函数。我该怎么办?
回答by Biju Kunjummen
You can probably try DataSourceUtils.getConnection(dataSource)
, per the APIit should return you the current connection for the datasource.
您可能可以尝试DataSourceUtils.getConnection(dataSource)
,根据API,它应该返回数据源的当前连接。
Update:Based on your comments and the source code for org.springframework.transaction.support.TransactionSynchronizationManager
:
更新:根据您的评论和源代码org.springframework.transaction.support.TransactionSynchronizationManager
:
Like I said, the key to getting the connection is the datasource name, if this cannot be obtained, one way out by looking at the source code is to try this:
就像我说的,获取连接的关键是数据源名称,如果无法获取,则查看源代码的一种方法是尝试以下方法:
TransactionSynchronizationManager.getResourceMap()
will return a map of datasource to ConnectionHolder in the current thread, assuming that you have only 1 resource involved in transaction, you can probably do a map.values().get(0)
to get the first ConnectionHolder, from which you can get a connection by calling .getConnection()
TransactionSynchronizationManager.getResourceMap()
会在当前线程返回一个datasource的map给ConnectionHolder,假设你只有1个资源参与事务,你大概可以做一个map.values().get(0)
获取第一个ConnectionHolder,从中你可以通过调用获取连接.getConnection()
So essentially calling the following:
所以基本上调用以下内容:
TransactionSynchronizationManager.getResourceMap().values().get(0).getConnection()
TransactionSynchronizationManager.getResourceMap().values().get(0).getConnection()
There probably has to be better way though :-)
不过,可能必须有更好的方法:-)
回答by parsifal
(completely rewritten based on comment thread; not sure why my original answer was focused on Hibernate, other than that's what I'm working with right now)
(根据评论线程完全重写;不知道为什么我原来的答案集中在 Hibernate 上,而不是我现在正在使用的)
The transaction manager is completely orthogonal to data sources. Some transaction managers interact directly with data sources, some interact through an intermediate layer (eg, Hibernate), and some interact through services provided by the container (eg, JTA).
事务管理器与数据源完全正交。有些事务管理器直接与数据源交互,有些通过中间层交互(例如,Hibernate),有些通过容器提供的服务(例如,JTA)进行交互。
When you mark a method as @Transactional
, all that means is that Spring will generate a proxy when it loads your bean, and that proxy will be handed to any other class that wants to use your bean. When the proxy's method is invoked, it (the proxy) asks the transaction manager to either give it an outstanding transaction or create a new one. Then it calls your actual bean method. When your bean method returns, the proxy interacts with the transaction manager again, to either say "I can commit" or "I must rollback". There are twists to this process; for example, a transactional method can call another transactional method and share the same transaction.
当您将方法标记为 时@Transactional
,意味着 Spring 将在加载您的 bean 时生成一个代理,并且该代理将被传递给任何其他想要使用您的 bean 的类。当代理的方法被调用时,它(代理)要求事务管理器给它一个未完成的事务或创建一个新的事务。然后它调用您的实际 bean 方法。当您的 bean 方法返回时,代理再次与事务管理器交互,以说“我可以提交”或“我必须回滚”。这个过程有曲折;例如,一个事务方法可以调用另一个事务方法并共享同一个事务。
While the transaction manager interacts withthe DataSource
, it does not ownthe DataSource
. You cannot ask the transaction manager to give you a connection. Instead, you must inject a frame-specific object that will return connections (such as the Hibernate SessionFactory
). Alternatively, you can use the static transaction-aware utility classes, but these again are tied to a specific framework.
虽然事务管理器交互使用的DataSource
,它不拥有的DataSource
。您不能要求事务管理器为您提供连接。相反,您必须注入将返回连接的特定于帧的对象(例如 Hibernate SessionFactory
)。或者,您可以使用静态事务感知实用程序类,但它们同样与特定框架相关联。
回答by Nandkumar Tekale
I assume you are using Plain Jdbc, you need to do is :
我假设您使用的是普通 Jdbc,您需要做的是:
BaseDao {
@Autowired
private DataSource dataSource;
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
public Connection getConnection() {
// ....use dataSource to create connection
return DataSourceUtils.getConnection(dataSource);
}
}
FooDao extends BaseDao {
// your foo() method
void foo() {
Connection conn = getConnection();
//....
}
}
回答by user1050755
What's a bit irritating about all of this is that the Spring docs are written mostly in marketing language that hides the ugliness behind the scenes.
所有这一切有点令人恼火的是,Spring 文档主要是用营销语言编写的,隐藏了幕后的丑陋。
In clear words:
明确地说:
your data source is stored in thread local context based on the (mostly valid) assumption that requests are always processed by a unique thread.
您的数据源存储在线程本地上下文中,这是基于(大多数有效)假设请求始终由唯一线程处理。
So, what Spring does in a pretty complicated way, is to store your stuff locally to your current execution thread, which is a trivial thing to do, but not clearly enough repeated throughout the spring docs. Spring basically puts your stuff into a "global context" to avoid pulling it through all of your interfaces and method definitions. It looks a bit magic at first, but is really just makeup.
因此,Spring 以一种非常复杂的方式所做的是将您的内容本地存储到您当前的执行线程中,这是一件微不足道的事情,但在整个 Spring 文档中重复得不够清楚。Spring 基本上把你的东西放到一个“全局上下文”中,以避免将它拉到你所有的接口和方法定义中。乍一看有点神奇,但实际上只是化妆。
Therefore you end up with a static DataSourceUtils method call to retrieve your stuff.
因此,您最终会调用静态 DataSourceUtils 方法来检索您的内容。
回答by Amir Pashazadeh
If you are using Spring Transaction with JDBC, configure a JdbcTemplate
, and access to current transaction using JdbcTemplate.execute(ConnectionCallback)
. That's the standard way of accessing a connection which is configured by Spring.
如果您将 Spring Transaction 与 JDBC 一起使用,请配置JdbcTemplate
, 并使用JdbcTemplate.execute(ConnectionCallback)
. 这是访问由 Spring 配置的连接的标准方式。