Spring多数据源事务失效复盘:@Transactional 异常无法回滚
一、问题现象
项目存在两套独立数据源,全局公共数据源、业务自定义数据源。
- 业务方法添加
@Transactional并指定自定义事务管理器,接口无任何报错。 - 代码主动抛出异常,数据库插入数据无法回滚。
二、相关业务配置代码
1. 全局数据源配置类
@Configuration
@MapperScan(basePackages = "com.**.mapper", sqlSessionTemplateRef = "sSqlSessionTemplate")
public class DruidConfiguration {
@Bean(name = "BData")
public DataSource BData() {
return new DruidDataSource();
}
}
2. 业务自定义数据源配置类
@Configuration
public class CustomDataSourceConfig {
@Bean("mDataSource")
public DataSource mDataSource(){
return new DruidDataSource();
}
@Bean("mDataSourceTransactionManager")
public PlatformTransactionManager mTransactionManager(){
return new DataSourceTransactionManager(mDataSource());
}
}
3. 事务失效业务代码
@Transactional(rollbackFor = Exception.class, transactionManager = "mDataSourceTransactionManager")
public void batchInsert() {
for (int i = 0; i < 50; i++) {
bizMapper.insert(data);
}
throw new RuntimeException("测试事务回滚");
}
三、两种简单排查方式
方式一 数据库自测 无需写代码
- 在自定义业务库新建一张独有测试表,全局公共库不创建该表
- 调用Mapper执行该表插入SQL
- 抛出表不存在异常,说明Mapper使用全局数据源;能正常插入,说明使用自定义数据源
方式二 Debug断点查看
打断点在Mapper调用处,直接逐层查看绑定链路,直观看到当前Mapper使用的数据源
Mapper代理对象 -> MapperProxy -> sqlSession -> SqlSessionFactory -> Configuration -> Environment -> dataSource
四、客观问题事实
- 项目存在两个
@MapperScan,扫描范围存在重叠 - 当前业务Mapper实际绑定全局公共数据源
- 事务绑定自定义数据源,和Mapper执行SQL的数据源不一致
- 两个数据源相互独立,导致事务无法回滚数据库操作
五、问题难以排查的原因
- 手动指定事务管理器,规避了多事务管理器冲突报错,无显性错误提示
- 两套数据源业务表名、结构完全一致,不会出现表不存在的SQL异常
- 全程无报错,仅存在事务不回滚的隐性业务问题
六、解决方案
有效方案
修改业务Mapper文件夹名称,脱离全局通配扫描范围
单独为业务Mapper配置专属@MapperScan,绑定自定义数据源相关配置,事务即可正常回滚
无效方案
在同路径下新增精准路径@MapperScan,无法改变已绑定的数据源关系,解决不了问题
七、开发总结
- 多数据源项目,不使用
com.**.mapper全局通配扫描Mapper - 事务无报错但无法回滚,优先核查Mapper实际使用的数据源
- 多数据源场景,物理隔离Mapper目录是最稳妥的方式