mybatis plus + AOP 多数据源自动切换

mituan 2020-05-26

1. 数据库配置

# .yml 文件
spring:
  datasource:
    db1:
      type: com.alibaba.druid.pool.DruidDataSource
      driver-class-name: com.mysql.cj.jdbc.Driver
      jdbc-url: jdbc-url      # 配置
      username: root
      password: psw
    db2:
      type: com.alibaba.druid.pool.DruidDataSource
      driver-class-name: com.mysql.cj.jdbc.Driver
      jdbc-url: jdbc-url     # 配置
      username: root
      password: psw

2. 数据源枚举类

public enum DataSourceEnums {

    PRIMARY("primaryDataSource"),
    SECOND("secondDataSource");

    private String value;

    DataSourceEnums(String value){this.value=value;}

    public String getValue() {
        return value;
    }

}

3. mybatis plus Config 数据源切换类

@Configuration
@MapperScan(value = {"com.example.demo.dao"})
public class MybatisPlusConfig {

    @Bean(name = "primaryDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.db1")
    public DataSource primaryDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean(name = "secondDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.db2")
    public DataSource secondDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean(name = "multipleTransactionManager")
    @Primary
    public DataSourceTransactionManager multipleTransactionManager(@Qualifier("multipleDataSource") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

    /**
     * 动态数据源配置
     */
    @Bean(name = "multipleDataSource")
    @Primary
    public DataSource multipleDataSource(@Qualifier("primaryDataSource") DataSource primaryDataSource,
                                         @Qualifier("secondDataSource") DataSource secondDataSource) {
        DataSourceContextHolder dynamicDataSource = new DataSourceContextHolder();
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put(DataSourceEnums.PRIMARY.getValue(), primaryDataSource);
        targetDataSources.put(DataSourceEnums.SECOND.getValue(), secondDataSource);
        dynamicDataSource.setTargetDataSources(targetDataSources);
        dynamicDataSource.setDefaultTargetDataSource(secondDataSource); // 程序默认数据源,这个要根据程序调用数据源频次,经常把常调用的数据源作为默认
        return dynamicDataSource;
    }

    @Bean("sqlSessionFactory")
    public SqlSessionFactory sqlSessionFactory() throws Exception {
        MybatisSqlSessionFactoryBean sqlSessionFactory = new MybatisSqlSessionFactoryBean();
        sqlSessionFactory.setDataSource(multipleDataSource(primaryDataSource(), secondDataSource()));

        MybatisConfiguration configuration = new MybatisConfiguration();
        configuration.setJdbcTypeForNull(JdbcType.NULL);
        //是否使用转驼峰
        configuration.setMapUnderscoreToCamelCase(true);
        configuration.setCacheEnabled(false);
        sqlSessionFactory.setConfiguration(configuration);//扫描 mapper 路径
        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        Resource[] resource = resolver.getResources("classpath:mapper/**/*.xml");
        sqlSessionFactory.setMapperLocations(resource);
        return sqlSessionFactory.getObject();
    }
}

 3. DataSource 数据源获取类

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

@Slf4j
public class DataSourceContextHolder extends AbstractRoutingDataSource {
    public static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();

    /**
     * 获取数据源
     */
    @Override
    protected Object determineCurrentLookupKey() {
        log.info("当前选择的数据源是:" + contextHolder.get());
        return contextHolder.get();
    }

    /**
     *  设置数据源*/
    public static void setDataSource(String db){
        contextHolder.set(db);
    }

    /**
     * 取得当前数据源
     * @return
     */
    public static String getDataSource(){
        return contextHolder.get();
    }

    /**
     * 清除上下文数据
     */
    public static void clear(){
        contextHolder.remove();
    }

}

4. AOP切面

import com.example.demo.config.DataSourceContextHolder;
import com.example.demo.enums.DataSourceEnums;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

@Component
@Aspect
@Order(-100) //这是为了保证AOP在事务注解之前生效,Order的值越小,优先级越高
@Slf4j
public class AOP {

    /**
     * mybatis plus 动态切换数据源
     * */
    // 定义切点
    @Before("execution(* com.example.demo.dao.db1..*(..))")
    public void db1() {
        log.info("数据源切换到db1...");
        DataSourceContextHolder.setDataSource(DataSourceEnums.PRIMARY.getValue());
    }

    @Before("execution(* com.example.demo.dao.db2..*(..))")
    public void db2() {
        log.info("数据源切换到db2...");
        DataSourceContextHolder.setDataSource(DataSourceEnums.SECOND.getValue());
    }

}

相关推荐