Java 踩坑 1|Spring 事务导致多数据源切换失败
背景
在我的日常开发中,遇到了同一个应用需要接入多数据源,因此需要指定不同数据访问层的数据源,而同时又需要在服务层保证事务,因此在服务层方法上使用了 @Transactional 注解,然而在实际执行时却没有切换数据源。
场景复现
这里为了简便,直接使用开源组件 dynamic-datasource-spring-boot-starter 实现多数据源切换,大家也可以自己动手实现。
1)创建 SpringBoot 工程;
2)引入 dynamic-datasource 依赖:
3)按照 dynamic-datasource 规范添加数据源配置;
4)添加 DDL 及数据:
4)创建数据对象及 Mapper,这里只列出了 Master 数据源数据对象及 Mapper;
5)新增服务层构建场景;
6)添加单元测试复现问题;这里仅列举了 @Transactional 导致异常的 UT;
复现代码链接:https://github.com/itschenxiang/spring-boot-examples/tree/main/multi-datasource
根因分析
Spring 开启事务后会维护一个 ConnectionHolder,保证在整个事务下,都是用同一个数据库连接。也就是说:使用了 @Transactional,Spring 会保证整个事务下都复用同一个 connection。
需要额外注意的是:单库的事务仍然可用,只要事务下不切换数据源即可。
解决方案
对于确实需要单事务多数据源的场景,解决方案包括:
从文章标题就可以看到的解决方案:删除事务注解;
Seata 事务;
其实对于大部分合理业务场景,应用可能有多个数据源,但基本都是单数据源事务。我实际遇到的也是单数据源事务,数据源切换失败的问题,根本原因是自定义数据源切换切面执行顺序在 @Transactional 之后,导致无法切换数据源(参考链接 1)。
参考链接
版权声明: 本文为 InfoQ 作者【itschenxiang】的原创文章。
原文链接:【http://xie.infoq.cn/article/1070db419729fac89b832c38c】。未经作者许可,禁止转载。
评论