Mybatis多数据源(二)使用AbstractRoutingDataSource实现动态数据源切换
一、原理#
Spring boot提供了AbstractRoutingDataSource 根据用户定义的规则选择当前的数据源,这样我们可以在执行数据库操作之前,设置使用的数据源,
即可实现数据源的动态路由。它的抽象方法determineCurrentLookupKey() 决定使用哪个数据源。
二、具体实现#
具体的业务代码不贴了,就放一下构造动态数据源的部分
1、DynamicDataSource继承AbstractRoutingDataSource,重写determineCurrentLookupKey方法#
/**动态数据源 * 扩展 Spring 的 AbstractRoutingDataSource 抽象类,重写 determineCurrentLookupKey 方法 * determineCurrentLookupKey() 方法决定使用哪个数据源 * */ public class DynamicDataSource extends AbstractRoutingDataSource { /** * ThreadLocal 用于提供线程局部变量,在多线程环境可以保证各个线程里的变量独立于其它线程里的变量。 * 也就是说 ThreadLocal 可以为每个线程创建一个【单独的变量副本】,相当于线程的 private static 类型变量。 */ private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>(); /** * 决定使用哪个数据源之前需要把多个数据源的信息以及默认数据源信息配置好 * * @param defaultTargetDataSource 默认数据源 * @param targetDataSources 目标数据源 */ public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources) { super.setDefaultTargetDataSource(defaultTargetDataSource); super.setTargetDataSources(targetDataSources); super.afterPropertiesSet(); } @Override protected Object determineCurrentLookupKey() { return getDataSource(); } public static void setDataSource(String dataSource) { CONTEXT_HOLDER.set(dataSource); } public static String getDataSource() { return CONTEXT_HOLDER.get(); } public static void clearDataSource() { CONTEXT_HOLDER.remove(); } }
2、将动态数据源注入到Spring容器中#
/** * 配置多数据源 */ @Configuration public class DynamicDataSourceConfig { private static final String FIRST = "first"; private static final String SECOND = "second"; @Bean @ConfigurationProperties("spring.datasource.druid.first") public DataSource firstDataSource(){ return DruidDataSourceBuilder.create().build(); } @Bean @ConfigurationProperties("spring.datasource.druid.second") public DataSource secondDataSource(){ return DruidDataSourceBuilder.create().build(); } @Bean @Primary public DynamicDataSource dataSource(DataSource firstDataSource, DataSource secondDataSource) { Map<Object, Object> targetDataSources = new HashMap<>(5); targetDataSources.put(FIRST, firstDataSource); targetDataSources.put(SECOND, secondDataSource); return new DynamicDataSource(firstDataSource, targetDataSources); } }
3、注解 + aop,指定本次数据库操作使用的数据源#
/** * 多数据源注解 * 指定要使用的数据源 * */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface CurDataSource { String name() default ""; } /** * 多数据源,切面处理类 * * @author xiaohe * @version V1.0.0 */ @Slf4j @Aspect @Component public class DataSourceAspect implements Ordered { private static final String FIRST = "first"; private static final String SECOND = "second"; @Pointcut("@annotation(com.hc.datasource.CurDataSource)") public void dataSourcePointCut() { } @Around("dataSourcePointCut()") public Object around(ProceedingJoinPoint point) throws Throwable { MethodSignature signature = (MethodSignature) point.getSignature(); Method method = signature.getMethod(); CurDataSource ds = method.getAnnotation(CurDataSource.class); if (ds == null) { DynamicDataSource.setDataSource(FIRST); log.debug("set datasource is " + FIRST); } else { DynamicDataSource.setDataSource(ds.name()); log.debug("set datasource is " + ds.name()); } try { return point.proceed(); } finally { DynamicDataSource.clearDataSource(); log.debug("clean datasource"); } } @Override public int getOrder() { return 1; } }
4、yml文件#
spring: application: name: test datasource: druid: type: com.alibaba.druid.pool.DruidDataSource driverClassName: com.mysql.jdbc.Driver first: url: jdbc:mysql://xxxx username: root password: root second: url: jdbc:mysql://xxx username: root password: root mybatis: mapper-locations: classpath:mapper/*.xml
三、测试#
service在调用mapper方法时,需要通过注解指定本次操作使用的数据源
@Service public class TestService { @Autowired TestMapper testMapper; @CurDataSource(name = "first") public List<CategoryEntity> list() { List<CategoryEntity> list = testMapper.list(); return list; } @CurDataSource(name = "second") public List<OrderEntity> orders() { List<OrderEntity> list = testMapper.orders(); return list; } }
访问 http://localhost:8080/categorys
查看日志
表明本次请求确实发生了数据源的切换
四、缺点#
数据源的实例化做的不太好,本次就是有几个数据源,就手动实例化几个数据源。如果数据源很多的话,一个个构造的话很麻烦
下篇文章批量构造多个数据源
地址:https://gitee.com/houchen1996/abstract-routing-data-source-test
分类:
|--- MyBatis
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
2018-05-07 c++ 动态生成string类型的数组