想象一下这段代码:
foo() {
Connection conn = ...;
}
已从具有注释 foo()
的方法调用了
@Transactional
。如何获取当前的 JDBC 连接?请注意, foo()
在 bean 中(因此它可以具有 @Autowired
字段),但 foo()
不能有参数(因此我无法从某处传递连接)。
[编辑] 我正在使用 jOOQ,它需要数据源或连接。我的问题:我不知道配置了哪个事务管理器。它可以是任何东西; Java EE,基于DataSource,通过JNDI获取数据源。我的代码不是应用程序,而是库。我需要吞下别人放在我盘子里的东西。同样,我无法请求 Hibernate 会话工厂,因为使用我的应用程序可能不使用 Hibernate。
但我知道其他代码,例如 Spring Hibernate 集成,可以以某种方式从事务管理器获取当前连接。我的意思是,Hibernate 不支持 Spring 的事务管理器,因此粘合代码必须使 Spring API 适应 Hibernate 的期望。我需要做同样的事情,但我不知道它是如何工作的。
[EDIT2]我知道有一个活动事务(即Spring在某处有一个Connection实例,或者至少有一个可以创建一个事务管理器),但我的方法不是@Transactional。我需要调用一个以
java.sql.Connection
作为参数的构造函数。我该怎么办?
您可以尝试
DataSourceUtils.getConnection(dataSource)
,根据API,它应该返回数据源的当前连接。
更新: 根据您的评论和
org.springframework.transaction.support.TransactionSynchronizationManager
的源代码:
就像我说的,获取连接的关键是数据源名称,如果无法获取,查看源代码的一种方法是尝试以下操作:
TransactionSynchronizationManager.getResourceMap()
将在当前线程中返回数据源到ConnectionHolder的映射,假设您只有1个资源参与事务,您可能可以执行map.values().get(0)
来获取第一个ConnectionHolder,从中您可以通过以下方式获取连接打电话.getConnection()
所以本质上调用以下内容:
TransactionSynchronizationManager.getResourceMap().values().get(0).getConnection()
可能有更好的方法:-)
事务管理器与数据源完全正交。有些事务管理器直接与数据源交互,有些通过中间层(例如 Hibernate)交互,有些通过容器提供的服务(例如 JTA)交互。
当你将一个方法标记为
@Transactional
时,这意味着 Spring 将在加载你的 bean 时生成一个代理,并且该代理将被传递给任何其他想要使用你的 bean 的类。当代理的方法被调用时,它(代理)要求事务管理器给它一个未完成的事务或创建一个新的事务。然后它调用您实际的 bean 方法。当您的 bean 方法返回时,代理再次与事务管理器交互,表示“我可以提交”或“我必须回滚”。 这个过程有一些曲折;例如,一个事务方法可以调用另一个事务方法并共享相同的事务。
虽然事务管理器与DataSource
交互,但它并不拥有
DataSource
。您不能要求事务管理器为您提供连接。相反,您必须注入一个特定于框架的对象,该对象将返回连接(例如 Hibernate
SessionFactory
)。或者,您可以使用静态事务感知实用程序类,但这些又与特定框架相关联。
编辑:我根据评论线程完全重写了我的答案;不知道为什么我最初的答案集中在 Hibernate 上,除了我现在正在使用的。
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();
//....
}
}
用明确的话说:
您的数据源存储在线程本地上下文中,基于(大多数有效的)假设:请求始终由唯一的线程处理。
因此,Spring 以相当复杂的方式所做的就是将您的内容存储在当前执行线程的本地,这是一件微不足道的事情,但在整个 Spring 文档中重复得不够清楚。 Spring 基本上将您的内容放入“全局上下文”中,以避免通过所有接口和方法定义来拉动它。乍一看有点神奇,但其实只是化妆而已。
因此,您最终会调用静态 DataSourceUtils 方法来检索您的内容。
JdbcTemplate
,并使用
JdbcTemplate.execute(ConnectionCallback)
访问当前事务。这是访问 Spring 配置的连接的标准方式。
@Component
public class MyServiceNonTransactional {
@Autowired
private TransactionTemplate transactionTemplate;
@Autowired
private DataSource dataSource;
public void doStuff() {
transactionTemplate.executeWithoutResult(status -> {
Connection connection = DataSourceUtils.getConnection(dataSource);
// here we go...
});
}
}
@Service
public class MyServiceTransactional {
@Autowired
private DataSource dataSource;
@Transactional
public void doStuff() {
Connection connection = DataSourceUtils.getConnection(dataSource);
// here we go...
}
}
@Service
public class MyServiceViaJdbc {
@Autowired
private JdbcTemplate jdbcTemplate;
public void doStuff() {
jdbcTemplate.execute((ConnectionCallback<Void>) conn -> {
// "conn" here we go!
return null;
});
}
}