Spring @Transactional事务内多JdbcTemplate调用数据不可见问题求解
问题修复方案
同个Spring声明式事务下,JdbcTemplate执行的前序写入对后续操作不可见,本质是两个操作没有真正使用同一个事务绑定的数据库连接,按以下步骤排查修复即可:
1. 修正事务基础配置
- 确认
DataSourceTransactionManager引用的数据源,和JdbcTemplate注入的数据源是Spring容器中的同一个单例Bean,禁止重复创建数据源实例,否则事务管理器无法把数据库连接绑定到线程上下文,JdbcTemplate每次操作都会从数据源拿新连接,自然看不到其他连接上未提交的写入。 - 非Spring Boot项目需要在配置类上加
@EnableTransactionManagement注解开启声明式事务支持;Spring Boot项目引入spring-boot-starter-jdbc或spring-boot-starter-data-jpa依赖后会自动开启,无需额外配置。 - 确认Service层加了
@Transactional的doMutlipleCalls方法是public修饰,且是通过Spring注入的Service代理Bean外部调用的,同一个类内部自调用该方法会导致AOP事务拦截失效。 - 如果使用MySQL数据库,确认业务表的存储引擎是InnoDB,MyISAM引擎不支持事务,操作会自动提交无法回滚,也不存在事务内可见性的说法。
2. 修正事务传播级别配置
不要在DAO层的类或方法上随意加@Transactional注解,事务边界统一放在Service层控制即可。如果业务需要在DAO层加事务注解,必须使用默认的Propagation.REQUIRED传播级别,禁止配置REQUIRES_NEW(会新开独立事务提交)、NOT_SUPPORTED(会挂起外层事务以非事务模式执行)这类会脱离外层事务的传播级别,否则DAO方法会独立于Service层的事务运行,既无法实现同事务数据可见,也无法正常回滚。
3. 优化跨方法传值逻辑
如果method2需要依赖method1插入的记录数据(比如自增主键),不要在method2里重新查询库表获取,直接改造method1返回插入结果,在Service层把结果传给method2即可,既规避可见性风险,也减少不必要的数据库查询,参考代码:
// Service层代码 @Transactional public void doMutlipleCalls() { // 拿到插入操作生成的主键/返回结果 Long insertedId = aDao.method1(); // 直接把结果传给更新方法 aDao.method2(insertedId); }
import org.springframework.jdbc.support.GeneratedKeyHolder; import org.springframework.jdbc.support.KeyHolder; import java.sql.PreparedStatement; import java.sql.Statement; public class MyDAO { // 改造method1,返回插入生成的主键 public Long method1() { KeyHolder keyHolder = new GeneratedKeyHolder(); jdbcTemplate.update(connection -> { PreparedStatement ps = connection.prepareStatement( "insert into your_table (col1, col2) values (?, ?)", Statement.RETURN_GENERATED_KEYS ); ps.setString(1, "业务值1"); ps.setInt(2, 2); return ps; }, keyHolder); return keyHolder.getKey().longValue(); } // 改造method2,接收传入的主键直接执行更新 public void method2(Long bizId) { jdbcTemplate.update( "update your_table set col1 = ? where id = ?", "更新后的值", bizId ); } }
验证方式
可以临时在两个DAO方法中打印当前操作使用的数据库连接对象,同个事务下两次拿到的连接hash值一定是一致的,如果不一致说明事务绑定失败,回头检查数据源和事务管理器的配置即可。配置正确后,method2抛出异常时,method1的插入操作会自动随事务回滚。
内容的提问来源于stack exchange,提问作者Thierry B
相关产品推荐
相关产品推荐

