mybatis-plus 动态数据源,mybatisplus 动态数据源

  mybatis-plus 动态数据源,mybatisplus 动态数据源

  

1、mybatis-plus @DS实现动态切换数据源原理

首先mybatis-plus使用com。窦米宝。动态。数据来源。抽象路由数据源继承抽象数据源接管数据源;具体实现类为com。窦米宝。动态。数据来源。动态路由数据源。项目初始化调用公共同步void addDataSource(字符串数据源数据源)加载数据源,数据源存进数据源地图中private MapString,data source数据源映射=new linked hashmap();

 

  private MapString,DynamicGroupDataSource组数据源=new concurrent hashmap();public synchronized void add数据源(String ds,data source数据源){ if(P6间谍){ data source=新P6数据源(data source);} dataSourceMap.put(ds,数据源);if (ds.contains下划线)){ String group=ds.split(下划线)[0];if(分组数据源。包含键(组)){组数据源。获得(团体).添加数据源(数据源);} else { try { dynamicgroupdata source group data source=new dynamicgroupdata source(group,strategy。new instance());分组数据源。添加数据源(data source);groupDataSources.put(组,组数据源);} catch(异常e){ log。error( dynamic-data source-添加名为[{ }]error 的数据源,ds,e);数据源映射。移除(ds);} } }日志。info( dynamic-data source-load a data source name[{ }]success ,ds);}进行数据操作时,方法会被com。窦米宝。动态。数据来源。AOP。dynamicdata source annotationinterceptor拦截,

  公共类dynamicdata sourceannotationinterceptor实现方法拦截器{/* * * SPEL的标识. private static final String DYNAMIC _ PREFIX= # ;private static final DynamicDataSourceClassResolver=new DynamicDataSourceClassResolver();@ Setter私有ds处理器ds处理器;@覆盖公共对象调用(方法调用调用)抛出throwable { try { dynamic data source context holder。push(determined data source(invocation));返回祈祷。proceed();}最后是{ dynamicdatasourcecontextholder。poll();} }私有字符串determineDatasource(方法调用)抛出throwable { Method Method=invocation。get方法();DS ds

   = method.isAnnotationPresent(DS.class)        ? method.getAnnotation(DS.class)        : AnnotationUtils.findAnnotation(RESOLVER.targetClass(invocation), DS.class);    String key = ds.value();    return (!key.isEmpty() && key.startsWith(DYNAMIC_PREFIX)) ? dsProcessor        .determineDatasource(invocation, key) : key;  }}拦截器首先从被拦截的方法或者类(一般@DS注解用于Service,也可用于Mapper和Controller)上寻找@DS注解,获取到@DS注解的值后将其存入com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;DynamicDataSourceContextHolder使用ThreadLocal存储当前线程的数据源名。

  

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

进行数据操作时,会调用org.springframework.jdbc.datasource.getConnection()方法;getConnection()方法最终调用了com.baomidou.dynamic.datasource.AbstractRoutingDataSource的getConnection()方法;

 

  

 @Override public Connection getConnection() throws SQLException { return determineDataSource().getConnection(); }

determineDataSource()由子类com.baomidou.dynamic.datasource.DynamicRoutingDataSource实现,可以看到DynamicRoutingDataSource从DynamicDataSourceContextHolder获取数据源名称,这个在之前拦截器处理存进ThreadLocal中,如果有数据源名称则从dataSourceMap中获取,没有则获取默认的primary数据源。

 

  

public DataSource determineDataSource() {    return getDataSource(DynamicDataSourceContextHolder.peek());}public DataSource getDataSource(String ds) {    if (StringUtils.isEmpty(ds)) {        return determinePrimaryDataSource();    } else if (!groupDataSources.isEmpty() && groupDataSources.containsKey(ds)) {        log.debug("dynamic-datasource switch to the datasource named [{}]", ds);        return groupDataSources.get(ds).determineDataSource();    } else if (dataSourceMap.containsKey(ds)) {        log.debug("dynamic-datasource switch to the datasource named [{}]", ds);        return dataSourceMap.get(ds);    }    if (strict) {        throw new RuntimeException("dynamic-datasource could not find a datasource named" + ds);    }    return determinePrimaryDataSource();}private DataSource determinePrimaryDataSource() {    log.debug("dynamic-datasource switch to the primary datasource");    return groupDataSources.containsKey(primary) ? groupDataSources.get(primary)        .determineDataSource() : dataSourceMap.get(primary);}

此时的数据源已经切换成了我们需要的数据源。

 

  数据操作完成后,方法返回第二步中的拦截器,执行DynamicDataSourceContextHolder.poll();清除掉此次的数据源,避免影响后续数据操作。

  附上动态数据源相关配置

  

spring: application: name: datasource: dynamic: primary: dataSource1 datasource: dataSource1: type: com.alibaba.druid.pool.DruidDataSource driverClassName: com.microsoft.sqlserver.jdbc.SQLServerDriver url: jdbc:sqlserver://localhost:1433;database=dataSource1 username: password: dataSource2: type: com.alibaba.druid.pool.DruidDataSource driverClassName: com.microsoft.sqlserver.jdbc.SQLServerDriver url: jdbc:sqlserver://localhost:1433;instanceName=sqlserver2017;DatabaseName=dataSource2 username: password:

pom.xml

 

  

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

相应类

 

  

@Service//@DS("dataSource2") 放在类上就是类下所有方法都使用这个数据源。public class XXXServiceImpl extends BaseServiceImpl<XXXMapper, XXXBean> implements XXXService {    @DS("dataSource1")    public void selectDataFromSource1() {       // do somethinng;    }        @DS("dataSource2")    public void selectDataFromSource1() {       // do somethinng;    }}

**注意:**不可在事务中切换数据库,保证事务需要方法使用同一连接,使用@DS(dataSource1)方法调用@DS(dataSource2)无法切换连接,会导致方法报错。

 

  到此这篇关于mybatis-plus @DS实现动态切换数据源原理的文章就介绍到这了,更多相关mybatis-plus @DS动态切换数据源内容请搜索盛行IT以前的文章或继续浏览下面的相关文章希望大家以后多多支持盛行IT!

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: