想象一下这段代码:
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
为参数的构造函数。我该怎么办?
答案 0 :(得分:8)
(完全基于评论线程重写;不知道为什么我的原始答案集中在Hibernate上,除了那是我现在正在使用的内容)
事务管理器与数据源完全正交。一些事务管理器直接与数据源交互,一些通过中间层(例如,Hibernate)进行交互,一些交互管理器通过容器提供的服务(例如,JTA)进行交互。
当您将方法标记为@Transactional
时,所有这意味着Spring在加载您的bean时将生成代理,并且该代理将被传递给任何其他想要使用您的bean的类。当调用代理的方法时,它(代理)要求事务管理器为其提供未完成的事务或创建新的事务。然后它调用你的实际bean方法。当bean方法返回时,代理再次与事务管理器交互,要么说“我可以提交”,要么“我必须回滚”。这个过程有些曲折;例如,事务方法可以调用另一个事务方法并共享同一个事务。
当事务管理器与 DataSource
进行交互时,它不会拥有 DataSource
。您不能要求事务管理器为您提供连接。相反,您必须注入一个将返回连接的特定于帧的对象(例如Hibernate SessionFactory
)。或者,您可以使用静态事务感知实用程序类,但这些类又与特定框架相关联。
答案 1 :(得分:6)
您可以尝试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()
可能必须有更好的方法: - )
答案 2 :(得分:4)
我假设您使用的是Plain 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();
//....
}
}
答案 3 :(得分:3)
对所有这些都有点恼火的是,Spring文档主要是用营销语言编写的,它隐藏了幕后的丑陋。
用清楚的话说:
您的数据源存储在线程本地上下文中,基于(通常是有效的)假设请求始终由唯一线程处理。
所以,Spring以一种非常复杂的方式做的是将你的东西本地存储到你当前的执行线程中,这是一件很容易的事情,但在整个春季文档中并没有明显的重复。 Spring基本上将您的东西放入“全局上下文”中,以避免将其拉入所有接口和方法定义。起初看起来有点神奇,但实际上只是化妆。
因此,您最终会使用静态DataSourceUtils方法调用来检索您的内容。
答案 4 :(得分:2)
如果您正在使用Spring事务与JDBC,请配置JdbcTemplate
,并使用JdbcTemplate.execute(ConnectionCallback)
访问当前事务。这是访问由Spring配置的连接的标准方式。
答案 5 :(得分:0)
3种其他方式:
@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;
});
}
}