使用baomidou @DS切换数据库

使用baomidou @DS切换数据库

首页模拟经营代号DS更新时间:2024-05-09

项目中经常会遇到一些多数据源的问题,如何在项目中切换呢?

baomidou就是个不错的切换数据源的组件。

pom文件配置

<dependency> <groupId>com.baomidou</groupId> <artifactId>dynamic-datasource-spring-boot-starter</artifactId> <version>3.5.0</version> </dependency>

项目配置(使用duri连接池)

spring: dataSource: dynamic: primary: mysql p6spy: true lazy: true druid: web-stat-filter: enabled: false stat-view-servlet: enabled: false max-active: 100 min-idle: 10 max-wait: 5000 pool-prepared-statements: true max-pool-prepared-statement-per-connection-size: 20 time-between-eviction-runs-millis: 600000 min-evictable-idle-time-millis: 300000 test-while-idle: true test-on-borrow: false test-on-return: false filter: stat: log-slow-sql: true slow-sql-millis: 3000 merge-sql: true wall: config: multi-statement-allow: true initial-size: 10 fail-fast: true kill-when-socket-read-timeout: true phy-timeout-millis: 3000 break-after-acquire-failure: false not-full-timeout-retry-count: -1 connection-error-retry-attempts: 0 remove-abandoned: true remove-abandoned-timeout: 180 datasource: mysql: type: com.alibaba.druid.pool.DruidDataSource driverClassName: com.mysql.cj.jdbc.Driver url: jdbc:mysql://127.0.0.1“3306/test?useSSL=false&requireSSL=false&characterEncoding=UTF-8&rewriteBatchedStatements=true username: root password: 12345678 oracle: type: com.alibaba.druid.pool.DruidDataSource driverClassName: oracle.jdbc.driver.OracleDriver url: jdbc:oracle:thin:@127.0.0.1:1521:orcl username: system password: 12345678

代码中使用

@DS("oracle") public List<AShareEODPrices> getTodayTrade(){ List<AShareEODPrices> list= pricesMapper.getAll(); return list; }

注: @DS注解可以加在方法、类上。

@DS注解其实通过AOP做的代理,来实现切换数据库源的。

所以,事务、定时任务这些场景使用@DS注解其实并不生效。

目前来说切换数据源失效主要有以下几个场景:(网上查到的)

  1. .不能使用事务,否则数据源不会切换,使用的还是是第一次加载的数据源 。删除 操作多数据源的方法或者类、接口 上的 注解 @Transactional() 即可。
  2. 操作其它数据源时,如果数据源不存在,使用的还是上一次加载的数据源
  3. 数据源名称最好不要包含下滑线,下滑线的数据源切换不了

那么问题来了。。。 现在我需要在定时任务以及内部线程使用@DS切换数据源,怎么办???

先分析一下baomidou的源码。

主要看两个类 :

/** * Core Interceptor of Dynamic Datasource * * @author TaoYu * @since 1.2.0 */ public class DynamicDataSourceAnnotationInterceptor implements MethodInterceptor { /** * The identification of SPEL. */ private static final String DYNAMIC_PREFIX = "#"; private final DataSourceClassResolver dataSourceClassResolver; private final DsProcessor dsProcessor; public DynamicDataSourceAnnotationInterceptor(Boolean allowedPublicOnly, DsProcessor dsProcessor) { dataSourceClassResolver = new DataSourceClassResolver(allowedPublicOnly); this.dsProcessor = dsProcessor; } @Override public Object invoke(MethodInvocation invocation) throws Throwable { String dsKey = determineDatasourceKey(invocation); DynamicDataSourceContextHolder.push(dsKey);//看这里 看这里。 他push了dskey。这个就是注解@DS设置的数据库名em。。 try { return invocation.proceed(); } finally { DynamicDataSourceContextHolder.poll();//看这里 他释放了,所以下面我们要看DynamicDataSourceContextHolder这个是啥了 } } private String determineDatasourceKey(MethodInvocation invocation) { String key = dataSourceClassResolver.findDSKey(invocation.getMethod(), invocation.getThis()); return (!key.isEmpty() && key.startsWith(DYNAMIC_PREFIX)) ? dsProcessor.determineDatasource(invocation, key) : key; } }

/** * 核心基于ThreadLocal的切换数据源工具类 * * @author TaoYu Kanyuxia * @since 1.0.0 */ public final class DynamicDataSourceContextHolder { /** * 为什么要用链表存储(准确的是栈) * <pre> * 为了支持嵌套切换,如ABC三个service都是不同的数据源 * 其中A的某个业务要调B的方法,B的方法需要调用C的方法。一级一级调用切换,形成了链。 * 传统的只设置当前线程的方式不能满足此业务需求,必须使用栈,后进先出。 * </pre> */ private static final ThreadLocal<Deque<String>> LOOKUP_KEY_HOLDER = new NamedThreadLocal<Deque<String>>("dynamic-datasource") { @Override protected Deque<String> initialValue() { return new ArrayDeque<>(); } }; private DynamicDataSourceContextHolder() { } /** * 获得当前线程数据源 * * @return 数据源名称 */ public static String peek() { return LOOKUP_KEY_HOLDER.get().peek(); } /** * 设置当前线程数据源 * <p> * 如非必要不要手动调用,调用后确保最终清除 * </p> * * @param ds 数据源名称 */ public static String push(String ds) { String dataSourceStr = StringUtils.isEmpty(ds) ? "" : ds; LOOKUP_KEY_HOLDER.get().push(dataSourceStr); return dataSourceStr; } /** * 清空当前线程数据源 * <p> * 如果当前线程是连续切换数据源 只会移除掉当前线程的数据源名称 * </p> */ public static void poll() { Deque<String> deque = LOOKUP_KEY_HOLDER.get(); deque.poll(); if (deque.isEmpty()) { LOOKUP_KEY_HOLDER.remove(); } } /** * 强制清空本地线程 * <p> * 防止内存泄漏,如手动调用了push可调用此方法确保清除 * </p> */ public static void clear() { LOOKUP_KEY_HOLDER.remove(); } }

看看源码上的注释说的很清楚了,其内部就是通过threadlocal来做的数据源切换。那么@DS注解其实就是为了给当前进程设置threadlocal,所以为什么这一步我们不能自己做呢???

所以手动切换数据库

public class UploadStockPrisesThread extends Thread{ @Override public void run() { //手动设置pracle数据库 DynamicDataSourceContextHolder.push("oracle"); try { //doJob }catch (Exception e) { e.printStackTrace(); }finally { //自己释放 DynamicDataSourceContextHolder.poll(); } } }

这样就可以在异步线程、定时任务中随意切换数据源了

查看全文
大家还看了
也许喜欢
更多游戏

Copyright © 2024 妖气游戏网 www.17u1u.com All Rights Reserved