乐趣区

关于java:MyBatisPlus-动态数据源

MyBatis-Plus 动静数据源须要引入 jar 包

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

应用 @DS()注解,括号内的值是咱们配置的数据源名称,通常应用的时候是配置到 mapper 层的类上;如果没有该注解则应用的是默认数据库;
@DS 的优先级也是就近准则,如果类上已有 @DS 且他的某个办法也有 @DS 那么该办法应用的数据库为办法上申明的数据库;
有些时候不同库中有雷同的表(对应一个实体类),须要在运行时动静抉择数据源。咱们能够在应用 @DS 注解的时候,应用 #通配符,将咱们的数据库以参数的形式来启用;# 参数名称能够动静的应用咱们的参数变量或者 header 和 session 中的变量;

‘#’参数的实现依赖于 com.baomidou.dynamic.datasource.processorDsProcessor 接口解决,实现形式有三个

  1. DsHeaderProcessor: 从申请的 header 中获取 ds 数据源名称。
  2. DsSessionProcessor: 从 session 中获取数据源 d 名称
  3. DsSpelExpressionProcessor: 通过 spel 表达式获取 ds 数据源名称,

这 3 种形式采纳的是责任链形式间断获取的。顺次执行。


DsHeaderProcessor 的拦挡格局为 @DS("#header 变量名")
@DS("#header dataName")
List<Entity> List(@Param("query") String query

DsSessionProcessor 的拦挡格局为 @DS("#session 变量名")
@DS("#session dataName")
List<Entity> List(@Param("query") String query

@DS("#dataName")
List<Entity> List(@Param("query") String query, String dataName);

其实现原理为 DynamicDataSourceAutoConfiguration 配置类 Advisor

  @Bean
  @ConditionalOnMissingBean
  public DynamicDataSourceAnnotationAdvisor dynamicDatasourceAnnotationAdvisor(DsProcessor dsProcessor) {DynamicDataSourceAnnotationInterceptor interceptor = new DynamicDataSourceAnnotationInterceptor();
    interceptor.setDsProcessor(dsProcessor);
    DynamicDataSourceAnnotationAdvisor advisor = new DynamicDataSourceAnnotationAdvisor(interceptor);
    advisor.setOrder(properties.getOrder());
    return advisor;
  }

DynamicDataSourceAnnotationAdvisor 的切点为 DS 注解

  private Pointcut buildPointcut() {Pointcut cpc = new AnnotationMatchingPointcut(DS.class, true);
    Pointcut mpc = AnnotationMatchingPointcut.forMethodAnnotation(DS.class);
    return new ComposablePointcut(cpc).union(mpc);
  }

拦截器为 DynamicDataSourceAnnotationInterceptor,拦截器会解决以 #结尾的注解值,并交由
DsProcessor 来解决

  private static final String DYNAMIC_PREFIX = "#";

  private String determineDatasource(MethodInvocation invocation) throws Throwable {Method method = invocation.getMethod();
    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;
  }

DsHeaderProcessor 解决逻辑为从 request 的 header 中取变量值:

  private static final String HEADER_PREFIX = "#header";

  @Override
  public boolean matches(String key) {return key.startsWith(HEADER_PREFIX);
  }

  @Override
  public String doDetermineDatasource(MethodInvocation invocation, String key) {HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder
        .getRequestAttributes()).getRequest();
// 这一行能够看出 HEADER_PREFIX 和数据源之间须要空一位,能够使任意字符
    return request.getHeader(key.substring(8));
  }

DsSessionProcessor 从 seesion 中去取变量的值:

  private static final String SESSION_PREFIX = "#session";

  @Override
  public boolean matches(String key) {return key.startsWith(SESSION_PREFIX);
  }

  @Override
  public String doDetermineDatasource(MethodInvocation invocation, String key) {HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder
        .getRequestAttributes()).getRequest();
// 这一行能够看出 SESSION_PREFIX 和数据源之间须要空一位,能够使任意字符
    return request.getSession().getAttribute(key.substring(9)).toString();}

DsSpelExpressionProcessor:


  /**
   * 参数发现器
   */
  private static final ParameterNameDiscoverer NAME_DISCOVERER = new DefaultParameterNameDiscoverer();
  /**
   * Express 语法解析器
   */
  private static final ExpressionParser PARSER = new SpelExpressionParser();

  @Override
  public boolean matches(String key) {return true;}

  @Override
  public String doDetermineDatasource(MethodInvocation invocation, String key) {Method method = invocation.getMethod();
    Object[] arguments = invocation.getArguments();
    EvaluationContext context = new MethodBasedEvaluationContext(null, method, arguments,
        NAME_DISCOVERER);
    final Object value = PARSER.parseExpression(key).getValue(context);
    return value == null ? null : value.toString();}

具体的实现原理能够参考 Spring 动静数据源,源码入口为:`
com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceAutoConfiguration

退出移动版