当前位置: 首页 > news >正文

Springboot 多数据源事务

起因

在一个service方法上使用的事务,其中有方法是调用的多数据源orderDB

image.png
image.png
image.png
image.png

但是多数据源没有生效,而是使用的primaryDB

image.png

原因

spring 事务实现的方式

@Transactional 注解为例 (也可以看 TransactionTemplate, 这个流程更简单一点)。
入口:ProxyTransactionManagementConfiguration
(从 config 类入手,需要哪些bean一目了然,然后直接顺着看下去就可以了)
主要有以下3个bean

  • TransactionAttri``buteSource:实现是 AnnotationTransactionAttributeSource, 提供从(存在 @Transactional 注解的)方法上读取事务的属性(注解的属性)的功能
  • TransactionInterceptor:事务方法拦截器的bean,在执行事务方法时,转到 (TransactionAspectSupport#invokeWithinTransaction) 方法,即spring事务处理的主要逻辑。
  • BeanFactoryTransactionAttributeSourceAdvisor:一个advisor(包含一个 Pointcut 切点和一个 Advice 通知),advice就是上面的事务拦截器,Pointcut 切点匹配能通过 TransactionAttributeSource 获取到事务信息的方法。

拦截器逻辑大概如下:
image.png

解决方案

每个数据源手动配置SqlSessionFactory

这种方式是通过手动声明创建orm框架对应的bean来实现多数据源的操作,即每个数据源都自己手动创建一套对用的bean。
不支持多个数据源事务,手动配置较繁琐
(如果使用的spring而不是springboot的话,就不会有这种多数据源的疑问,因为本来就要自己声明bean)

动态数据源(本次使用)

只需要把@Transactional(rollbackFor = Exception.class) 换为@DSTransactional即可
并且抛出异常事务也会回滚

image.png

动态数据源实现原理

同样看一下 DynamicDataSourceAutoConfiguration 这个配置相关的类就大概了解了。

  • DynamicRoutingDataSource: 动态数据源,内部使用 Map 保存了多个数据源。获取 connection 时,根据 ThreadLocal 中的 dsKey 获取对应的数据源
    • 另:对于多数据源事务 (TransactionContext.getXID() isNotEmpty),会返回一个 ConnectionProxy 并暂存到 ConnectionFactory 中, 该 ConnectionProxy 不会执行 commit、rollback、close 操作事务相关的方法。
public Connection getConnection() throws SQLException {String xid = TransactionContext.getXID();if (StringUtils.isEmpty(xid)) {// 非多数据源事务直接获取对应 connectionreturn determineDataSource().getConnection();} else {String ds = DynamicDataSourceContextHolder.peek();ds = StringUtils.isEmpty(ds) ? "default" : ds;// 多数据源事务,使用代理的 connection (屏蔽了 commit 等操作)ConnectionProxy connection = ConnectionFactory.getConnection(ds);return connection == null ? getConnectionProxy(ds, determineDataSource().getConnection()) : connection;}
}// 获取 代理的 connection, 并将其存入 ConnectionFactory, 内部维护一个 ThreadLocal<Map>, 同时会 setAutoCommit(false) 开启事务
private Connection getConnectionProxy(String ds, Connection connection) {ConnectionProxy connectionProxy = new ConnectionProxy(connection, ds);ConnectionFactory.putConnection(ds, connectionProxy);return connectionProxy;
}// DynamicRoutingDataSource
// 从 ThreadLocal 获取当前 dsKey 然后获取对应 datasource
public DataSource determineDataSource() {String dsKey = DynamicDataSourceContextHolder.peek();return getDataSource(dsKey);
}
  • DynamicDataSourceAnnotationInterceptor: 处理 @DS 注解的拦截器,获取 @DS 指定的 datasource 并存入 ThreadLocal 中, 供 DynamicRoutingDataSource 使用
  • dynamicTransactionAdvisor: 处理 @DSTransactional 多数据源事务注解的拦截器,在执行目标方法前,标记为多数据源事务 (TransactionContext.bind(xid)), 执行完后, 通知 ConnectionFactory 中的 connectionProxy 进行事务的 commit 或 rollback。
// DynamicLocalTransactionAdvisor
public Object invoke(MethodInvocation methodInvocation) throws Throwable {if (!StringUtils.isEmpty(TransactionContext.getXID())) {return methodInvocation.proceed();}// 事务是否成功boolean state = true;Object o;String xid = UUID.randomUUID().toString();// 标记当前为 多数据源事务TransactionContext.bind(xid);try {o = methodInvocation.proceed();} catch (Exception e) {state = false;throw e;} finally {// 通知 connectionProxy 进行 commit 或 rollbackConnectionFactory.notify(state);TransactionContext.remove();}return o;
}

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 代码随想录算法训练营day22 | 77. 组合、216.组合总和III 、17.电话号码的字母组合
  • kettle从入门到精通 第八十一课 ETL之kettle kettle中的json对象字段写入postgresql中的json字段正确姿势
  • CTF之网站被黑
  • Unity 之 【Android Unity 共享纹理】之 Android 共享图片给 Unity 显示
  • 大厂面经:滴滴大数据面试题及参考答案(3万字长文)
  • 返回倒数第 k 个节点 - 力扣(LeetCode)C语言
  • 记录|博图中VB脚本和子程序之间的区别?
  • 原生JavaScript系列面试题
  • MyBatis-Plus的基本使用(一)
  • uni-app pinia搭建
  • Vue3开源Tree组件研发:节点勾选支持v-model
  • 防火墙——SNAT和DNAT策略的原理及应用、防火墙规则的备份、还原和抓包
  • python基础---1.变量、运算符和表达式、基本数据结构
  • 基于Orangepi全志H616开发嵌入式数据库——SQLite
  • Android Button设置点击监听器用switch case R.id.xxxx报错:Constant expression required
  • (十五)java多线程之并发集合ArrayBlockingQueue
  • 《用数据讲故事》作者Cole N. Knaflic:消除一切无效的图表
  • 【140天】尚学堂高淇Java300集视频精华笔记(86-87)
  • 【391天】每日项目总结系列128(2018.03.03)
  • Angular6错误 Service: No provider for Renderer2
  • flutter的key在widget list的作用以及必要性
  • JavaScript 基本功--面试宝典
  • jquery ajax学习笔记
  • Mac 鼠须管 Rime 输入法 安装五笔输入法 教程
  • 阿里云爬虫风险管理产品商业化,为云端流量保驾护航
  • 安装python包到指定虚拟环境
  • 等保2.0 | 几维安全发布等保检测、等保加固专版 加速企业等保合规
  • 给github项目添加CI badge
  • 检测对象或数组
  • 前端代码风格自动化系列(二)之Commitlint
  • 要让cordova项目适配iphoneX + ios11.4,总共要几步?三步
  • 用简单代码看卷积组块发展
  • ​520就是要宠粉,你的心头书我买单
  • ​Java并发新构件之Exchanger
  • # 睡眠3秒_床上这样睡觉的人,睡眠质量多半不好
  • #565. 查找之大编号
  • #大学#套接字
  • (14)学习笔记:动手深度学习(Pytorch神经网络基础)
  • (1综述)从零开始的嵌入式图像图像处理(PI+QT+OpenCV)实战演练
  • (C++17) std算法之执行策略 execution
  • (Matlab)基于蝙蝠算法实现电力系统经济调度
  • (阿里巴巴 dubbo,有数据库,可执行 )dubbo zookeeper spring demo
  • (纯JS)图片裁剪
  • (待修改)PyG安装步骤
  • (二)Eureka服务搭建,服务注册,服务发现
  • (二)构建dubbo分布式平台-平台功能导图
  • (十六)视图变换 正交投影 透视投影
  • (学习日记)2024.03.25:UCOSIII第二十二节:系统启动流程详解
  • (一)认识微服务
  • (自用)gtest单元测试
  • .Net Attribute详解(上)-Attribute本质以及一个简单示例
  • .NET BackgroundWorker
  • .NET Core 项目指定SDK版本
  • .NET Core 中插件式开发实现
  • .NET/C# 判断某个类是否是泛型类型或泛型接口的子类型