SpringBoot+MybatisPlus多数据源动态切换

公司某项目做大屏展示,但数据来源自7个不同的数据库,需要涉及跨库查询,要求。

本项目采用SpringBoot+MybatisPlus做服务端提供RESTful接口,前后端分离开发,总结一下项目中实现的动态数据源切换的实现方式。

首先在application文件中配置数据源

spring:
  aop:
    proxy-target-class: true
    auto: true
  datasource:
    druid:
      # 一期数据源
      db1:
        url: jdbc:oracle:thin:@172.16.3.131:1521:orcl
        username: ZHJGDSJ
        password: ZHJGDSJ
        driver-class-name: oracle.jdbc.driver.OracleDriver
        initialSize: 5
        minIdle: 5
        maxActive: 20
      # 二期 诚信监管
      db2:
        url: jdbc:oracle:thin:@172.17.2.119:1521:orcl
        username: cxjg_test1
        password: 123456
        driver-class-name: oracle.jdbc.driver.OracleDriver
        initialSize: 5
        minIdle: 5
        maxActive: 20
      # 二期 协同监管
      db3:
        url: jdbc:oracle:thin:@172.17.2.119:1521:orcl
        username: xtjg_test
        password: 123456
        driver-class-name: oracle.jdbc.driver.OracleDriver
        initialSize: 5
        minIdle: 5
        maxActive: 20
      # 二期 公示系统
      db4:
        url: jdbc:oracle:thin:@172.17.2.119:1521:orcl
        username: GSGS
        password: 123456
        driver-class-name: oracle.jdbc.driver.OracleDriver
        initialSize: 5
        minIdle: 5
        maxActive: 20
      # 二期 省局-浪潮库
      db5:
        url: jdbc:oracle:thin:@172.16.1.26:1521:orcl
        username: GSYW
        password: 123456
        driver-class-name: oracle.jdbc.driver.OracleDriver
        initialSize: 5
        minIdle: 5
        maxActive: 20
      # 二期 市局-浪潮库
      db6:
        url: jdbc:oracle:thin:@172.16.1.65:1521:orcl
        username: GSYWSJ
        password: 123456
        driver-class-name: oracle.jdbc.driver.OracleDriver
        initialSize: 5
        minIdle: 5
        maxActive: 20
      # 二期 正元
      db7:
        url: jdbc:oracle:thin:@172.17.2.119:1521:orcl
        username: ZHJGDSJ
        password: 123456
        driver-class-name: oracle.jdbc.driver.OracleDriver
        initialSize: 5
        minIdle: 5
        maxActive: 20

创建一个数据源枚举类

package com.rexen.di.management.common.type;

/**
 * @Author Jimmy
 * @date 2019/2/7.
 */
public enum DBTypeEnum {
    db1("db1"), db2("db2"), db3("db3"), db4("db4"), db5("db5"), db6("db6"), db7("db7");
    private String value;

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

    public String getValue() {
        return value;
    }
}

创建一个上下文来缓存当前数据源

package com.rexen.di.management.boot.config;

import com.rexen.di.management.common.type.DBTypeEnum;

/**
 * @Author Jimmy
 * @date 2019/2/7.
 */
public class DbContextHolder {
    private static final ThreadLocal contextHolder = new ThreadLocal<>();
    /**
     * 设置数据源
     * @param dbTypeEnum
     */
    public static void setDbType(DBTypeEnum dbTypeEnum) {
        contextHolder.set(dbTypeEnum.getValue());
    }

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

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

创建AbstractRoutingDataSource 的实现类实现切换

package com.rexen.di.management.boot.config;

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

/**
 * @Author Jimmy
 * @date 2019/2/7.
 */
public class DynamicDataSource extends AbstractRoutingDataSource {
    /**
     * 取得当前使用哪个数据源
     * @return
     */
    @Override
    protected Object determineCurrentLookupKey() {
        return DbContextHolder.getDbType();
    }
}

通过AOP方式实现拦截和切换,根据Mapper路径来决定切换到哪个数据源

package com.rexen.di.management.boot.advice;

import com.rexen.di.management.boot.config.DbContextHolder;
import com.rexen.di.management.common.annotation.DataSourceSwitch;
import com.rexen.di.management.common.type.DBTypeEnum;
import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.util.Objects;

/**
 * @Author Jimmy
 * @date 2019/2/7.
 */

@Component
@Aspect
@Order(-100)
public class DataSourceSwitchAspect {
    Logger log = Logger.getLogger(DataSourceSwitchAspect.class);

    @Pointcut("execution(* com.rexen.di.management.mapper..*.*(..))")
    private void db1Aspect() {
    }

    @Pointcut("execution(* com.rexen.di.management.custom2.mapper..*.*(..))")
    private void db2Aspect() {
    }
    @Pointcut("execution(* com.rexen.di.management.custom3.mapper..*.*(..))")
    private void db3Aspect() {
    }
    @Pointcut("execution(* com.rexen.di.management.custom4.mapper..*.*(..))")
    private void db4Aspect() {
    }
    @Pointcut("execution(* com.rexen.di.management.custom5.mapper..*.*(..))")
    private void db5Aspect() {
    }
    @Pointcut("execution(* com.rexen.di.management.custom6.mapper..*.*(..))")
    private void db6Aspect() {
    }
    @Pointcut("execution(* com.rexen.di.management.custom7.mapper..*.*(..))")
    private void db7Aspect() {
    }

    @Before( "db1Aspect()" )
    public void db1(JoinPoint joinPoint) {
        log.info("切换到db1 数据源...");
        setDataSource(joinPoint, DBTypeEnum.db1);
    }

    @Before("db2Aspect()" )
    public void db2 (JoinPoint joinPoint) {
        log.info("切换到db2 数据源...");
        setDataSource(joinPoint,DBTypeEnum.db2);
    }
    @Before("db3Aspect()" )
    public void db3 (JoinPoint joinPoint) {
        log.info("切换到db3 数据源...");
        setDataSource(joinPoint,DBTypeEnum.db3);
    }
    @Before("db4Aspect()" )
    public void db4 (JoinPoint joinPoint) {
        log.info("切换到db4 数据源...");
        setDataSource(joinPoint,DBTypeEnum.db4);
    }
    @Before("db5Aspect()" )
    public void db5 (JoinPoint joinPoint) {
        log.info("切换到db5 数据源...");
        setDataSource(joinPoint,DBTypeEnum.db5);
    }
    @Before("db6Aspect()" )
    public void db6 (JoinPoint joinPoint) {
        log.info("切换到db6 数据源...");
        setDataSource(joinPoint,DBTypeEnum.db6);
    }
    @Before("db7Aspect()" )
    public void db7 (JoinPoint joinPoint) {
        log.info("切换到db7 数据源...");
        setDataSource(joinPoint,DBTypeEnum.db7);
    }

    /**
     * 添加注解方式,如果有注解优先注解,没有则按传过来的数据源配置
     * @param joinPoint
     * @param dbTypeEnum
     */
    private void setDataSource(JoinPoint joinPoint, DBTypeEnum dbTypeEnum) {
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        DataSourceSwitch dataSourceSwitch = methodSignature.getMethod().getAnnotation(DataSourceSwitch.class);
        if (Objects.isNull(dataSourceSwitch) || Objects.isNull(dataSourceSwitch.value())) {
            DbContextHolder.setDbType(dbTypeEnum);
        }else{
            log.info("根据注解来切换数据源,注解值为:"+dataSourceSwitch.value());
            switch (dataSourceSwitch.value().getValue()) {
                case "db1":
                    DbContextHolder.setDbType(DBTypeEnum.db1);
                    break;
                case "db2":
                    DbContextHolder.setDbType(DBTypeEnum.db2);
                    break;
                case "db3":
                    DbContextHolder.setDbType(DBTypeEnum.db3);
                    break;
                case "db4":
                    DbContextHolder.setDbType(DBTypeEnum.db4);
                    break;
                case "db5":
                    DbContextHolder.setDbType(DBTypeEnum.db5);
                    break;
                case "db6":
                    DbContextHolder.setDbType(DBTypeEnum.db6);
                    break;
                case "db7":
                    DbContextHolder.setDbType(DBTypeEnum.db7);
                    break;
                default:
                    DbContextHolder.setDbType(dbTypeEnum);
            }
        }
    }
}

创建MybatisPlus的配置文件

package com.rexen.di.management.boot.config;

import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import com.baomidou.mybatisplus.MybatisConfiguration;
import com.baomidou.mybatisplus.entity.GlobalConfiguration;
import com.baomidou.mybatisplus.mapper.LogicSqlInjector;
import com.baomidou.mybatisplus.plugins.PaginationInterceptor;
import com.baomidou.mybatisplus.plugins.PerformanceInterceptor;
import com.baomidou.mybatisplus.spring.MybatisSqlSessionFactoryBean;
import com.rexen.di.management.common.type.DBTypeEnum;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.type.JdbcType;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;

/**
 * @Author Jimmy
 * @date 2019/2/6.
 */
@Configuration
@MapperScan({"com.rexen.di.management.mapper","com.rexen.di.management.custom2.mapper",
        "com.rexen.di.management.custom3.mapper","com.rexen.di.management.custom4.mapper",
        "com.rexen.di.management.custom5.mapper","com.rexen.di.management.custom6.mapper","com.rexen.di.management.custom7.mapper"})
public class MybatisPlusConfig {

    /**
     * mybatis-plus分页插件
* 文档:http://mp.baomidou.com
*/ @Bean public PaginationInterceptor paginationInterceptor() { PaginationInterceptor paginationInterceptor = new PaginationInterceptor(); //paginationInterceptor.setLocalPage(true);// 开启 PageHelper 的支持 return paginationInterceptor; } /** * mybatis-plus SQL执行效率插件【生产环境可以关闭】 */ @Bean public PerformanceInterceptor performanceInterceptor() { return new PerformanceInterceptor(); } @Bean(name = "db1") @ConfigurationProperties(prefix = "spring.datasource.druid.db1" ) public DataSource db1 () { return DruidDataSourceBuilder.create().build(); } @Bean(name = "db2") @ConfigurationProperties(prefix = "spring.datasource.druid.db2" ) public DataSource db2 () { return DruidDataSourceBuilder.create().build(); } @Bean(name = "db3") @ConfigurationProperties(prefix = "spring.datasource.druid.db3" ) public DataSource db3 () { return DruidDataSourceBuilder.create().build(); } @Bean(name = "db4") @ConfigurationProperties(prefix = "spring.datasource.druid.db4" ) public DataSource db4 () { return DruidDataSourceBuilder.create().build(); } @Bean(name = "db5") @ConfigurationProperties(prefix = "spring.datasource.druid.db5" ) public DataSource db5 () { return DruidDataSourceBuilder.create().build(); } @Bean(name = "db6") @ConfigurationProperties(prefix = "spring.datasource.druid.db6" ) public DataSource db6 () { return DruidDataSourceBuilder.create().build(); } @Bean(name = "db7") @ConfigurationProperties(prefix = "spring.datasource.druid.db7" ) public DataSource db7 () { return DruidDataSourceBuilder.create().build(); } /** * 动态数据源配置 * @return */ @Bean @Primary public DataSource multipleDataSource (@Qualifier("db1") DataSource db1, @Qualifier("db2") DataSource db2,@Qualifier("db3") DataSource db3,@Qualifier("db4") DataSource db4, @Qualifier("db5") DataSource db5,@Qualifier("db6") DataSource db6,@Qualifier("db7") DataSource db7) { DynamicDataSource dynamicDataSource = new DynamicDataSource(); Map< Object, Object > targetDataSources = new HashMap<>(); targetDataSources.put(DBTypeEnum.db1.getValue(), db1 ); targetDataSources.put(DBTypeEnum.db2.getValue(), db2); targetDataSources.put(DBTypeEnum.db3.getValue(), db3); targetDataSources.put(DBTypeEnum.db4.getValue(), db4); targetDataSources.put(DBTypeEnum.db5.getValue(), db5); targetDataSources.put(DBTypeEnum.db6.getValue(), db6); targetDataSources.put(DBTypeEnum.db7.getValue(), db7); dynamicDataSource.setTargetDataSources(targetDataSources); dynamicDataSource.setDefaultTargetDataSource(db1); return dynamicDataSource; } @Bean("sqlSessionFactory") public SqlSessionFactory sqlSessionFactory() throws Exception { MybatisSqlSessionFactoryBean sqlSessionFactory = new MybatisSqlSessionFactoryBean(); sqlSessionFactory.setDataSource(multipleDataSource(db1(),db2(),db3(),db4(),db5(),db6(),db7())); sqlSessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:/mapper/**/*Mapper.xml")); MybatisConfiguration configuration = new MybatisConfiguration(); //configuration.setDefaultScriptingLanguage(MybatisXMLLanguageDriver.class); configuration.setJdbcTypeForNull(JdbcType.NULL); configuration.setMapUnderscoreToCamelCase(true); configuration.setCacheEnabled(false); sqlSessionFactory.setConfiguration(configuration); sqlSessionFactory.setPlugins(new Interceptor[]{ //PerformanceInterceptor(),OptimisticLockerInterceptor() paginationInterceptor() }); sqlSessionFactory.setGlobalConfig(globalConfiguration()); return sqlSessionFactory.getObject(); } @Bean public GlobalConfiguration globalConfiguration() { GlobalConfiguration conf = new GlobalConfiguration(new LogicSqlInjector()); conf.setLogicDeleteValue("-1"); conf.setLogicNotDeleteValue("1"); conf.setIdType(0); conf.setMetaObjectHandler(new MyMetaObjectHandler()); conf.setDbColumnUnderline(true); conf.setRefresh(true); return conf; } }

 

你可能感兴趣的:(java)