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) 指定)
至此,相信大家已经弄清楚了有什么问题,以及如何解决它!
相关文章
- spring学习笔记(23)基于tx/aop配置切面增强事务
- Spring中注解事务方面的问题
- spring boot: 输出json
- spring boot: spring Aware的目的是为了让Bean获得Spring容器的服务
- Spring -- IOC
- spring boot:方法中使用try...catch导致@Transactional事务无效的解决(spring boot 2.3.4)
- Spring Cloud Alibaba | Nacos集群部署
- [Java Spring MVC] Introduction to interceptors
- spring boot单元测试之八:用mockmvc模拟header参数(spring boot 2.4.4)
- spring boot:使用poi导出excel电子表格文件(spring boot 2.3.1)
- spring aop切点表达式常见写法
- Spring7:基于注解的Spring MVC(下篇)
- spring中Constructor、@Autowired、@PostConstruct的顺序
- spring学习 8-面试(事务,解决线程安全)
- 在SAP ABAP里使用注解@Inject模拟Java Spring
- Docker with Spring Boot
- (转)Spring事务不生效的原因大解读
- SSM三大框架之Spring框架环境准备
- spring中事务传播下,特殊方法手动控制事务
- spring boot配置dubbo(XML)
- Spring之事务源码理解,Spring4.3.12.RELEASE版本
- 005-spring-data-elasticsearch 3.0.0.0使用【三】-spring-data之Spring数据扩展
- Spring MVC文件上传处理
- spring事务bug
- [spring学习]13、声明式事务(@Transaction)