11

想象一下这段代码:

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作为参数的构造函数。我该怎么办?

4

6 回答 6

14

您可能可以尝试DataSourceUtils.getConnection(dataSource),根据API,它应该返回数据源的当前连接。

更新: 根据您的评论和源代码org.springframework.transaction.support.TransactionSynchronizationManager

就像我说的,获取连接的关键是数据源名称,如果无法获取,查看源代码的一种方法是尝试这个:

TransactionSynchronizationManager.getResourceMap()将在当前线程中返回一个数据源映射到 ConnectionHolder,假设您只有一个资源参与事务,您可能可以做一个map.values().get(0)来获取第一个 ConnectionHolder,您可以通过调用从中获取连接.getConnection()

所以基本上调用以下内容:

TransactionSynchronizationManager.getResourceMap().values().get(0).getConnection()

不过可能必须有更好的方法:-)

于 2012-08-02T14:46:52.027 回答
9

(根据评论线程完全重写;不知道为什么我的原始答案集中在 Hibernate 上,除了我现在正在使用的)

事务管理器与数据源完全正交。一些事务管理器直接与数据源交互,一些通过中间层交互(例如,Hibernate),一些通过容器提供的服务(例如,JTA)交互。

当您将方法标记为@Transactional时,这意味着 Spring 在加载您的 bean 时将生成一个代理,并且该代理将被交给任何其他想要使用您的 bean 的类。当调用代理的方法时,它(代理)要求事务管理器给它一个未完成的事务或创建一个新事务。然后它调用你的实际 bean 方法。当您的 bean 方法返回时,代理再次与事务管理器交互,以说“我可以提交”或“我必须回滚”。这个过程有曲折。例如,一个事务方法可以调用另一个事务方法并共享同一个事务。

当事务管理器与 交互时,它DataSource并不拥有. DataSource你不能要求事务管理器给你一个连接。相反,您必须注入一个特定于框架的对象,该对象将返回连接(例如 Hibernate SessionFactory)。或者,您可以使用静态事务感知实用程序类,但这些又与特定框架相关联。

于 2012-08-02T15:08:05.317 回答
7

我假设您使用的是普通 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();
       //....
    }
}
于 2012-08-02T14:29:30.180 回答
5

所有这一切有点令人恼火的是,Spring 文档主要是用营销语言编写的,隐藏了幕后的丑陋。

用明确的话来说:

您的数据源存储在线程本地上下文中,基于(大多数有效的)请求始终由唯一线程处理的假设。

因此,Spring 以一种非常复杂的方式所做的就是将您的内容本地存储到您当前的执行线程中,这是一件微不足道的事情,但在整个 Spring 文档中重复得不够清楚。Spring 基本上将您的东西放入“全局上下文”中,以避免将其拉过所有接口和方法定义。一开始看起来有点神奇,但实际上只是化妆。

因此,您最终会调用静态 DataSourceUtils 方法来检索您的资料。

于 2012-08-07T09:35:23.347 回答
3

如果您将 Spring Transaction 与 JDBC 一起使用,请配置一个JdbcTemplate,并使用JdbcTemplate.execute(ConnectionCallback). 这是访问由 Spring 配置的连接的标准方式。

于 2012-08-02T14:28:10.523 回答
1

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;
        });
    }
        
}
于 2020-07-22T14:39:10.137 回答