Spring 下,关于动态数据源的事务问题的探讨
开心一刻
毒蛇和蟒蛇在讨论谁的捕猎方式最高效。
毒蛇:我只需要咬对方一口,一段时间内它就会逐渐丧失行动能力,最后死亡。
蟒蛇冷笑:那还得等生效时间,我只需要缠住对方,就能立刻致它于死地。
毒蛇大怒:你缠它身子,你下贱!
蟒蛇:你不也亲了它吗?

前情回顾
看着文章的标题,不知道大家能否想到具体是什么问题,如果你有点懵,那就对了! (你不懵的话我这篇文章就没存在的意义了,嘿嘿)
在给大家指出具体是什么问题时,我们先来回顾一些内容
Spring 事务原理
相信大家对这个都能说上来一些,Spring 事务是 Spring AOP 的一种具体应用,底层依赖的是动态代理
大致流程类似如下

通过代理对象来调用目标对象,而在代理对象中有事务相关的增强处理
具体细节可参考以下文章
结合 ThreadLocal 来看 Spring 事务源码,感受下清泉般的洗涤!
Spring 动态数据源原理
原理解密 → Spring AOP 实现动态数据源(读写分离),底层原理是什么中已经详细介绍过了,流程大体如下

Spring AOP → 将我们指定的 lookupKey 放入 ThreadLocal
ThreadLocal → 线程内共享 lookupKey
DynamicDataSource → 对多数据源进行封装,根据 ThreadLocal 中的 lookupKey 动态选择具体的数据源
有什么问题
既然事务和动态数据源都是 Spring AOP 的具体应用,那么代理就存在先后顺序了
要么是

要么是

我们来看看这两者有什么区别
事务在前,动态数据源在后
此时,事务的前置增强处理会先生效,那么此时开始事务获取的 Connection 从哪来 ? 肯定是从 DynamicDataSource 来,因为我们给事务管理器配置的就是它
@Bean
public PlatformTransactionManager transactionManager(@Qualifier("dynamicDataSource") DataSource dynamicDataSource) {
// 配置事务管理, 使用事务时在方法头部添加@Transactional注解即可
return new DataSourceTransactionManager(dynamicDataSource);
}
既然是从 DynamicDataSource 获取的 Connection,那 DynamicDataSource 根据 lookupKey 获取 Connection 的时候,会从 masterDataSource 数据源获取还是从 slaveDataSource 数据源获取 ?因为此时还未将 lookupKey 绑定到当前线程,那么 DynamicDataSource 会从默认数据源获取,而我们配置的默认数据源是 slaveDataSource
/**
* 获取当前线程的数据源
* @return
*/
public static DataSourceType getDataSourceType()
{
return HOLDER.get() == null ? DataSourceType.SLAVE : HOLDER.get();
}
说白了,此时的动态数据源对事务不生效,事务始终从默认数据源获取 Connection,而没有动态的效果,这就是问题了
Talk is cheap. Show me the code,我们来看看是不是真的如上所说

192.168.0.112 正是我们的从库,对应的就是我们的默认数据源 slaveDataSource
动态数据源在前,事务在后
此时,动态数据源的前置增强会先执行,DynamicDataSource 需要的 lookupKey 会先于事务绑定到当前线程,那么事务从 DynamicDataSource 获取 Connection 的时候就能根据当前线程的 lookupKey 来动态选择 masterDataSource 还是 slaveDataSource
此种情况是没有问题的
解决问题
总结下问题:如何保证事务中的动态数据源也有动态的效果,也就是如何保证动态数据源的前置增强先于事务
我们知道 Spring AOP 是能够指定顺序的,只要我们显示的指定动态数据源的 AOP 先于 事务的 AOP 即可;如何指定顺序,常用的方式是实现 Order 接口,或者使用 @Order 注解,Order 的值越小,越先执行,所以我们只需要保证动态数据源的 Order 值小于事务的 Order 值即可
我们先来看看事务的 Order 值默认是多少,在 EnableTransactionManagement 注解中
/**
* Indicate the ordering of the execution of the transaction advisor
* when multiple advices are applied at a specific joinpoint.
* <p>The default is {@link Ordered#LOWEST_PRECEDENCE}.
*/
int order() default Ordered.LOWEST_PRECEDENCE;
默认是最低级别(值非常大),那么我们只需要保证动态数据源的 Order 比这个值小就好,我们就取 1
@Component
@Slf4j
@Order(1)
public class DynamicDataSourceAspect {
我们在来看看是否真的可行

已经不是默认的 slaveDataSource ,而是我们指定的 masterDataSource(通过 @MasterSlave(MASTER) 指定)
至此,相信大家已经弄清楚了有什么问题,以及如何解决它
什么,还没理解 ? 你过来,我保证不打死你

总结
1、不只是动态数据源和事务,只要涉及到多个 AOP,就可能会有顺序问题,这是值得大家注意的
2、相关约束
主数据库执行 INSERT UPDATE DELETE 操作,可能还有部分 SELECT 操作(主从同步多少有延时)
从数据库只执行 SELECT 操作
默认数据源最好设置成主数据源,防止粗心将更新操作执行到了从数据库;楼主之所以设置成从数据源,是考虑到绝大多数数据库操作是查询,这样可以减少代码量;具体怎么选,需要大家结合实际情况来决定
Spring 下,关于动态数据源的事务问题的探讨的更多相关文章
- Spring Boot2.x 动态数据源配置
原文链接: Spring Boot2.x 动态数据源配置 基于 Spring Boot 2.x.Spring Data JPA.druid.mysql 的动态数据源配置Demo,适合用于数据库的读写分 ...
- 如何通过Spring Boot配置动态数据源访问多个数据库
之前写过一篇博客<Spring+Mybatis+Mysql搭建分布式数据库访问框架>描述如何通过Spring+Mybatis配置动态数据源访问多个数据库.但是之前的方案有一些限制(原博客中 ...
- Spring(AbstractRoutingDataSource)实现动态数据源切换--转载
原始出处:http://linhongyu.blog.51cto.com/6373370/1615895 一.前言 近期一项目A需实现数据同步到另一项目B数据库中,在不改变B项目的情况下,只好选择项目 ...
- Spring(AbstractRoutingDataSource)实现动态数据源切换
转自: http://blog.51cto.com/linhongyu/1615895 一.前言 近期一项目A需实现数据同步到另一项目B数据库中,在不改变B项目的情况下,只好选择项目A中切换数据源,直 ...
- spring AbstractRoutingDataSource实现动态数据源切换
使用Spring 提供的 AbstractRoutingDataSource 实现 创建 AbstractRoutingDataSource 实现类,负责保存所有数据源与切换数据源策略:public ...
- 原理解密 → Spring AOP 实现动态数据源(读写分离),底层原理是什么
开心一刻 女孩睡醒玩手机,收到男孩发来一条信息:我要去跟我喜欢的人表白了! 女孩的心猛的一痛,回了条信息:去吧,祝你好运! 男孩回了句:但是我没有勇气说不来,怕被打! 女孩:没事的,我相信你!此时女孩 ...
- Spring配置,JDBC数据源及事务
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.sp ...
- 【spring boot】SpringBoot初学(7)– 多数据源及其事务
前言 github: https://github.com/vergilyn/SpringBootDemo 代码位置: 参考: Spring Boot Reference Guide , §77.2 ...
- Spring 实现动态数据源切换--转载 (AbstractRoutingDataSource)的使用
[参考]Spring(AbstractRoutingDataSource)实现动态数据源切换--转载 [参考] 利用Spring的AbstractRoutingDataSource解决多数据源的问题 ...
随机推荐
- Python-文件操作-之优化购物车
#此次购物车优化,主要使用了文件操作的相关方法,有买家入口,和商家入口 一.买家入口 1.买家第一次启动程序输入金额,金额会记录到文件里,再登录就读取文件里保存的金额,买家可以购买商品,按 ‘q’ 退 ...
- 洛谷1972 HH的项链 树状数组查询区间内不同的数的数量
题目链接:https://www.luogu.com.cn/problem/P1972 题意大致是:给定一个序列长度为n,给出m个查询区间,要求响应是区间内不同的数的个数.为此我们考虑到树状数组的区间 ...
- DNS提供的服务
DNS提供的服务 DNS是:1.一个由分层的DNS服务器实现的分布式数据库:2. 一个使主机能够查询分布式数据库的应用协议.DNS协议运行在UDP上,使用53号端口. 与http,FTP,SMTP协议 ...
- C# 录音和播放录音-NAudio
在使用C#进行录音和播放录音功能上,使用NAudio是个不错的选择. NAudio是个开源,相对功能比较全面的类库,它包含录音.播放录音.格式转换.混音调整等操作,具体可以去Github上看看介绍和源 ...
- SFDC 401认证准备及考试
401认证准备及考试 刚过了401的认证,一些个人的体会,希望能帮助到准备过401的朋友. 1. 考试只是手段,不是目的.这个链接的视频请认真看完,http://www.salesforcetrain ...
- SpringBoot安装与配置
1.环境准备 1.1.Maven安装配置 Maven项目对象模型(POM),可以通过一小段描述信息来管理项目的构建,报告和文档的项目管理工具软件. 下载Maven可执行文件 cd /usr/local ...
- 用FME处理物探点表和线表,生成管线和设施
在项目的数据处理中,客户会提供物探点表和线表. 点表主要包括该点的物探编号.该点的X坐标.Y坐标.点的其他属性 线表主要包括该线的起始点物探编号.终止物探编号.线的其他属性 点表
- JVM 调优工具
JMeter LoadRunner 压力测试工具 JConsole 是一个内置 Java 性能分析器,可以查看内存,线程,类,CPU 等的使用情况,可以通过线程去查看线程的试用情况,死锁可以被检 ...
- RPC框架实现(一) Protobuf的rpc实现
概述 RPC框架是云端服务基础框架之一,负责云端服务模块之间的项目调用,类似于本地的函数调用一样方便.常见的RPC框架配带的功能有: 编解码协议.比如protobuf.thrift等等. 服务发现.指 ...
- 面向对象编程基础(java)
面向对象编程基础 1.1 面向对象概述 在程序开发初期,大家使用的是结构化开发语言,也就是面向过程(opp),但随着市场需求剧增,软件的规模也越来越大,结构化语言的弊端也暴露出来. 开发周期无休止的拖 ...