Mybatis-Plus中使用@DS注解动态选择数据源的源码解读
作者:labulaka24
一、使用场景
1、首先在yml文件中配置多个数据源
2、根据注解的Target可以知道
可以在我们对应的类上、方法上使用@DS注解(一般是用在mapper、service),使用过该注解的方法即可操作到对应的数据源
二、原理剖析(为什么使用过该注解的方法即可操作到对应的数据源)
1、对应源码所在包的位置
2、首先mybatis-plus使用
com.baomidou.dynamic.datasource.AbstractRoutingDataSource继承 AbstractDataSource接管数据源;
具体实现类(ctrl+alt+b可以查看抽象类或者的具体实现类)为com.baomidou.dynamic.datasource.DynamicRoutingDataSource。
项目初始化调用public synchronized void addDataSource(String ds, DataSource dataSource)加载数据源,数据源存进dataSourceMap中。
AbstracRoutingDataSource
DynamicRoutingDataSource
3、当我们发送具体的操作请求的时候
进行数据操作时,方法会被com.baomidou.dynamic.datasource.aop.DynamicDataSourceAnnotationInterceptor
拦截
DynamicDataSourceAnnotationInterceptor
拦截器首先从被拦截的方法或者类(一般@DS注解用于Service,也可用于Mapper和Controller)上寻找@DS注解,获取到@DS注解的值后将其存入
com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
DynamicDataSourceContextHolder使用ThreadLocal存储当前线程的数据源名。
3、进行数据操作时
会调用org.springframework.jdbc.datasource.getConnection()方法;
getConnection()方法最终调用了com.baomidou.dynamic.datasource.AbstractRoutingDataSource的getConnection()方法;
跟进方法我们可以看到determineDataSource()由子类com.baomidou.dynamic.datasource.DynamicRoutingDataSource实现,可以看到DynamicRoutingDataSource从DynamicDataSourceContextHolder获取数据源名称,这个在之前拦截器处理存进ThreadLocal中,如果有数据源名称则从dataSourceMap中获取,没有则获取默认的primary数据源。
此时的datasource已经切换成了我们需要的数据源
4、数据操作完成后
方法返回第二步中的拦截器,执行DynamicDataSourceContextHolder.poll();
清除掉此次Threadlocal中的数据源,避免影响后续数据操作。
特别注意:
不可在事务中切换数据库,保证事务需要方法使用同一连接,使用@DS(dataSourceOne)
方法调用@DS(dataSourceTwo)
无法切换连接,会导致方法报错。
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。