java切换数据源(多数据源)

现在要在一个服务里设置两个数据源:oracle和mysql
1.需要写配置文件:
@Controller主要是加载spring配置类
@EnableTransactionManagement事务用法,在访问数据库的service中加@Transactional
@MapperScan指定要变成实现类的接口所在的包,然后包下面的所有接口在编译之后都会生成相应的实现类

@Configuration
@EnableTransactionManagement(order = -200)
@MapperScan("com.csair.mpms.mapper*")
public class MyBatiesPlusConfiguration {

// 精确到 master 目录,以便跟其他数据源隔离
static final String MAPPER_LOCATION = "classpath:mapper/*.xml";

/*
 * 分页插件,自动识别数据库类型
 * 多租户,请参考官网【插件扩展】
 */
@Bean
public PaginationInterceptor paginationInterceptor() {
    PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
    // 开启 PageHelper 的支持
    //paginationInterceptor.setLocalPage(true);
    return paginationInterceptor;
}


@Bean(name = "masterTransactionManager")
    public DataSourceTransactionManager masterTransactionManager() {
        return new DataSourceTransactionManager(masterDataSource());
    }

@Bean(name = "masterSqlSessionFactory")
@Primary
public SqlSessionFactory masterSqlSessionFactory(@Qualifier("masterDataSource") DataSource masterDataSource)
        throws Exception {
    final MybatisSqlSessionFactoryBean sessionFactory = new MybatisSqlSessionFactoryBean();
    sessionFactory.setDataSource(multipleDataSource(masterDataSource(),db2()));
    MybatisConfiguration configuration = new MybatisConfiguration();
    //configuration.setDefaultScriptingLanguage(MybatisXMLLanguageDriver.class);
    configuration.setJdbcTypeForNull(JdbcType.NULL);
    configuration.setMapUnderscoreToCamelCase(true);
    configuration.setCacheEnabled(false);
    sessionFactory.setConfiguration(configuration);
    sessionFactory.setPlugins(new Interceptor[]{ //PerformanceInterceptor(),OptimisticLockerInterceptor()
            paginationInterceptor() //添加分页功能
    });
    sessionFactory.setMapperLocations(new
            PathMatchingResourcePatternResolver()
            .getResources(MAPPER_LOCATION));
    return sessionFactory.getObject();
}

    @Bean(name = "masterDataSource", destroyMethod = "close", initMethod = "init")
    @ConfigurationProperties(prefix = "spring.datasource.druid.db1" )
    public DataSource masterDataSource() {
        return DruidDataSourceBuilder.create().build();
    }

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

/**
 * 动态数据源配置
 * @return
 */
@Bean
@Primary
public DataSource multipleDataSource (@Qualifier("masterDataSource") DataSource mpmsDataSource,
                                      @Qualifier("db2") DataSource db2 ) {
    DynamicDataSource dynamicDataSource = new DynamicDataSource();
    Map< Object, Object > targetDataSources = new HashMap<>();
    targetDataSources.put(DBTypeEnum.db1.getValue(), mpmsDataSource );
    targetDataSources.put(DBTypeEnum.db2.getValue(), db2);
    dynamicDataSource.setTargetDataSources(targetDataSources);
    dynamicDataSource.setDefaultTargetDataSource(mpmsDataSource);
    return dynamicDataSource;
}
}

数据源:
DataSourceSwitch文件:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface DataSourceSwitch {
    DBTypeEnum value() default DBTypeEnum.db1;
}

DataSourceSwitchAspect文件:

@Component
@Aspect
@Order(-100)
@Slf4j
public class DataSourceSwitchAspect {

@Pointcut("execution(* com.csair.mpms.service.*.*(..))")
private void db1Aspect() {
}

@Pointcut("execution(* com.csair.mpms.db2Service.*.*(..))")
private void db2Aspect() {
}

@Pointcut("@annotation(DataSourceSwitch)")
private void dataSourceSwitch() {
}

@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("dataSourceSwitch()")
public void dataSourceSwitch(JoinPoint joinPoint) {
    MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
    DataSourceSwitch dataSourceSwitch = methodSignature.getMethod().getAnnotation(DataSourceSwitch.class);
    if (dataSourceSwitch.value() != null) {
        DbContextHolder.setDbType(dataSourceSwitch.value());
        log.info("切换到{} 数据源...", dataSourceSwitch.value().getValue());
    }
}

/**
 * 添加注解方式,如果有注解优先注解,没有则按传过来的数据源配置
 *
 * @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;
            default:
                DbContextHolder.setDbType(dbTypeEnum);
        }
    }
}

}

DbContextHolder文件:

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();
    }
}

DBTypeEnum文件:

public enum DBTypeEnum {
    db1("db1"), db2("db2");
    private String value;

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

public String getValue() {
    return value;
}

}

DynamicDataSource文件:

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

配置文件:

datasource:
    druid:
      db1:
        username: 
        password: 
        driver-class-name: oracle.jdbc.OracleDriver
        url: jdbc:oracle:thin:
        initialSize: 5
        minIdle: 5
        maxActive: 50
        max-wait: 3000
        # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
        timeBetweenEvictionRunsMillis: 60000
        # 配置一个连接在池中最小生存的时间,单位是毫秒
        minEvictableIdleTimeMillis: 300000
        validationQuery: SELECT 1 FROM DUAL
      db2:
        type: com.alibaba.druid.pool.DruidDataSource            # 当前数据源操作类型
        driver-class-name: com.mysql.cj.jdbc.Driver              # mysql驱动包
        url: