如何处理spring事务与多数据源冲突问题?

今天要说的问题就是@Transactional  和切换数据源 @DS 同时使用时 切换数据源无效的问题

当我在一个service方法上切换了数据源时同时 加入了事务 ,此时方法中需要操作两次数据库 需要保证事务 下图:

然后在加了 @Transactional  注解后出现问题: 数据源一直都是使用的默认数据源  而非我指定的 test数据源, 然后各种查资料发现 在同一个事务中数据源是无法切换的  最终发现在我debug源码时,

1在切换数据源之前 @Transactional 先执行 切面,此时会去获取数据源,而此时数据源还没有切换 就会获取默认的数据源 源码:

2在执行完@Transactional  锁定数据源之后, @ds切面后执行, 此时切换数据源的时候只是改变了缓存数据源配置的key字符串,在执行db操作的时候没有重新根据当前字符串的key去获取最新的数据源,所以表现出 数据源未切换的现象,查询报错表名不存在

思考: 如果在事务获取数据源之前,切换数据源问题不就解决了吗, 查了资料发现 在自定义的切面中 加入

然后再次进行测试, 数据源成功切换,事务仍然有效,问题解决

注意:此事务内只能保证同一个数据源, 非分布式事务

}

二、springboot整合多数据源解决分布式事务

1、pom文件 依赖引入

* 多数据源解决分布式事务测试 * 新增用户并生成订单(解决分布式事务问题) // 事务底层原理采用aop技术做增强 // 无需再指定某个事务管理器,全交给 Atomikos 全局事务 // order为数据库关键字,记得使用``

12、初始化sql文件


b、数据库结果 - 如下图所示 :

b、数据库结果 - 如下图所示 :

}

通过分库实现不同租户的业务数据隔离,在笔者的公司是一个集团数据库作为一个master库,集团拥有众多的分公司,每一个分公司都有一个slave库,从而做到集团的数据和各个分公司的数据库的数据隔离

  1. 项目默认使用加载集团master库(库中包含子公司信息表,存储了每一个分公司数据库的连接信息)来启动项目
  2. 监听项目的启动,当项目启动时,读取master库中的子公司的信息表,将子公司的数据库连接读取到系统中

默认集团数据源和事务管理器配置

到目前位置,在service业务中,已经可以通过设置ThreadLocal中的数据源key来切换数据源了,测试用例

测试结果可以发现dataA和dataB中的数据是不同的,说明数据一个来自master库,一个来自slave库

此处踩坑指的是事务的坑,这里分两种

  • A类 : 一个业务中只对一个数据库进行了增删改
  • B类 : 一个业务中同时对多个数据库进行了增删改

A类解决方式比较简单,无需特殊处理,直接使用@Transactional注解,将事务管理交给spring来管理,

B类业务在笔者公司的业务情况如下

  1. 当集团公司给子公司分配权限的时候
  2. 首先在集团库(master)中的[子公司权限中间表]中新增中间表关联数据
  3. 将分配的权限同步到子公司的数据库(slave)中

同时在master库和slave库中进行增删改操作,应该保证master库和slave库数据一致,需要事务支持,
但是如果使用@Transactional注解后,会发现assignPrivileges方法中DataSourceContext失效,无法切换数据源,原因是因为spring的事务管理使用了aop代理,在方法开始前,已经将当前数据源绑定在了线程中,所以无论怎样切换,使用的都是同一个数据源,spring事务管理代码截图如下
红框处spring的事务管理将数据源进行了线程绑定

B类解决方案【踩坑 & 核心内容】

不让spring管理事务,我们自己管理事务
解决思路,每次切换数据源之后,获取到数据源的数据库连接connection,将connection的自动提交设置成false,在业务完成后,统一的对所有的connection集体提交事务,如果异常,则集体对connection进行回滚操作

这里我们需要切换数据源后获取到数据源的连接,故此时需要用到之前我们自己维护的map中保存的数据源,在MultiRouteDataSource中添加一个获取数据源的方法getDataSource()

上面的方案听起来是不是和spring的实现方式很像,没错,其实可以借鉴aop使用前置通知、后置通知、异常处理来做到,但是笔者公司的业务使用到的数据源的个数是不定的,所以没有使用自定义注解+aop实现来简化操作,而是使用了函数式变成的思想,做了一个函数接口来完成,读者可以通过自己的业务,使用注解+aop改造以下代码

做完这一步,对以上的代码进行测试,会发现当一个service中使用了多个数据源对数据库进行操作后,并不能正常的回滚事务,原因如下

当事务不再由spring管理后,mybatis会使用自己的事务管理机制,即在操作完数据库后自动提交和关闭,所以解决方法就是重写Connection对象的提交和关闭方法,使mybatis的自动提交和关闭不生效


 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

笔者遇到mybatis自己管理事务的坑时,借鉴了一篇博客中的观点,博客如下:

}

我要回帖

更多关于 springboot多数据源事务 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信