通过Header控制MyBatis-Plus动态数据源

8

动态参数解释器说明

在MyBatis dynamic-datasource中,默认有三个职责链来处理动态参数解析器 header->session->spel

@DS("#session.tenantName")//从session获取
public List selectSpelBySession() {
	return userMapper.selectUsers();
}

@DS("#header.tenantName")//从header获取
public List selectSpelByHeader() {
	return userMapper.selectUsers();
}

@DS("#tenantName")//使用spel从参数获取
public List selectSpelByKey(String tenantName) {
	return userMapper.selectUsers();
}

@DS("#user.tenantName")//使用spel从复杂参数获取
public List selecSpelByTenant(User user) {
	return userMapper.selectUsers();
}

如果想让前端控制不同的数据库只需要用到第一个DsHeaderProcessor 就能实现。

实际开发场景需求

在开发可视化大屏中,由于不同地市连接不同地市的数据库,并且不同地市的表结构都是相同的情况下就可以使用@DS("#header.data-source") 注解,可以通过源码发现他只获取了#header8个字符串后面的字符。

package com.baomidou.dynamic.datasource.processor;

import org.aopalliance.intercept.MethodInvocation;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;

/**
 * @author TaoYu
 * @since 2.5.0
 */
public class DsHeaderProcessor extends DsProcessor {

    /**
     * header prefix
     */
    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();
        return request.getHeader(key.substring(8));
    }
}

request.getHeader(key.substring(8));当作动态数据源标识,matches 方法为是否匹配成功。这时候只需要和前端约定名为data-source 的header让前端去控制在*application.yml 中的* spring.datasource.dynamic.datasource.xxxxx xxxx就是与前端约定的data-source 来选择数据库。

从业务逻辑层去控制不同数据源

参考header解析器,继承DsProcessor,如果matches返回true则匹配成功,调用doDetermineDatasource返回匹配到的数据源,否则跳到写一个解析器.

  1. 自定义一个处理器类似DsHeaderProcessor 解析器。

  2. 重写完后重新注入一个根据自己解析顺序的解析处理器.

@Configuration
public class MyDynamicDataSourceConfig{

   @Bean
   public DsProcessor dsProcessor() {
        DsHeaderProcessor headerProcessor = new DsHeaderProcessor();
        DsSessionProcessor sessionProcessor = new DsSessionProcessor();
        DsSpelExpressionProcessor spelExpressionProcessor = new DsSpelExpressionProcessor();
        headerProcessor.setNextProcessor(sessionProcessor);
        sessionProcessor.setNextProcessor(spelExpressionProcessor);
        return headerProcessor;
   }
}

在Mapper接口下面的方法使用

public interface UserMapper{
    // 前缀可以是p0,a0
    @DS("#p0.tenantName")
    public List selecSpelByTenant(User user);
}

对于在接口下面的使用, 由于编译器的默认配置没有将接口参数的元数据写入字节码文件中.

所以spring el会无法识别参数名称, 只能用默认的参数命名方式

  1. 第一个参数: p0,a0,(加入-parameter后,可以使用参数具体的名字,例如这里的#user)

  2. 第二个参数: p1,a1

  3. 第三个参数: P2,a2

可以通过修改maven配置和java编译配置将接口参数信息写入字节码文件

maven配置:

<plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <!-- 想启用  <parameters>true</parameters> 的maven编译最低版本为:3.6.2 -->
        <version>3.6.2</version>
        <configuration>
            <source>${java.version}</source>
            <target>${java.version}</target>
            <parameters>true</parameters>
        </configuration>
    </plugin>