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
接口处理,实现方式有三个
- DsHeaderProcessor: 从请求的header中获取ds数据源名称。
- DsSessionProcessor: 从session中获取数据源d名称
- 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
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。