Java编程式事务中getConnection复用线程绑定连接:事务开启时获取并绑定到ThreadLocal,同一事务内多次调用均返回该连接,确保事务一致性;结束后归还连接池并清理ThreadLocal。

Java 中编程式事务底层的 getConnection 并不是直接从数据源“拿一个新连接”,而是由 Spring 的事务管理器(如 DataSourceTransactionManager)在开启事务时,**将连接绑定到当前线程,并复用该连接**。这个过程对开发者透明,但理解其机制有助于排查事务失效、连接泄漏等问题。
事务开启时:Connection 被获取并绑定到 ThreadLocal
当调用 TransactionTemplate.execute(...) 或手动使用 TransactionStatus 开启事务时,DataSourceTransactionManager 会:
- 调用
dataSource.getConnection()获取一个物理连接(可能来自连接池,如 HikariCP) - 将该连接封装为
ConnectionHolder,并存入TransactionSynchronizationManager的ThreadLocal<map object>></map>中(具体是resources变量) - 设置连接为手动提交(
setAutoCommit(false)),并标记为“已参与事务”
同一事务内:后续 getConnection() 都返回同一个绑定连接
只要在同一个事务上下文(即同一线程、且未挂起嵌套事务),无论你写多少次:
JdbcTemplate.query(...); // 内部通过 DataSourceUtils.getConnection(dataSource) 获取 DataSourceUtils.getConnection(dataSource); // 显式调用 jpaEntityManager.unwrap(Connection.class); // JPA 场景下,Hibernate 也会走同步管理器
它们最终都通过 DataSourceUtils.doGetConnection() 查找当前线程绑定的 ConnectionHolder,**直接返回已存在的连接,不再调用 dataSource.getConnection()**。
立即学习“Java免费学习笔记(深入)”;
这是保证“事务内所有操作共享同一数据库连接”的关键。
事务结束后:连接被释放或归还连接池
事务提交或回滚后,DataSourceTransactionManager 会:
- 调用
ConnectionHolder.release()—— 若连接是事务中首次获取的,则调用con.close()(实际是归还给连接池) - 清理
TransactionSynchronizationManager中的ThreadLocal绑定,避免内存泄漏 - 若连接是外部传入(如 JTA 场景),则不 close,只解绑
注意:别绕过 Spring 的 Connection 获取方式
以下写法会破坏事务一致性:
- ✘ 直接 new HikariDataSource().getConnection() —— 拿到的是未绑定、未禁用 auto-commit 的新连接,不在事务中
- ✘ 在 @Transactional 方法里手动 try-with-resources 获取连接 —— 容易提前 close,导致后续操作拿到不同连接或空指针
- ✘ 使用非 Spring 管理的 JdbcTemplate / MyBatis SqlSession —— 若未配置好事务同步,可能无法感知当前事务连接
正确做法是始终通过 Spring 提供的抽象获取连接:DataSourceUtils.getConnection()、JdbcTemplate、@PersistenceContext 注入的 EntityManager 等。



















