SpringBoot Mybatis 多数据源配置 注解方式切换数据源

SpringBoot Mybatis 多数据源配置 注解方式切换数据源

整合了下网上找到的各种方法,终于ojbk,Mark下,以免下次再次遇到

介绍

近期因项目需求,原有的SpringBoot项目需要连接到两个数据库获取数据,确定有如下方案:

  • DBLink -- 此方式适合同时两个oracle数据库,连接其中一个数据库,通过DBLink可以获取到另一个数据库的数据,但是这种方式只能在两个oracle数据库间使用,适用性不强,pass
  • 数据同步 -- 通过原有的数据库同步软件进行同步,这种方式可以支持在两个非oracle数据库间使用,但是同步资源消耗,对于实时性要求较大的数据不适用,pass
  • 如题,通过SpringBoot+Mybatis多数据源配置实现多数据源连接,采用注解方式,支持任意数据库,默认使用原数据库,原有代码无需变更,下边将详细介绍这种方式的配置方法。

项目结构

image.png

多数据源配置

Application新增注解,排除DataSourceAutoConfiguration,手动配置数据源

@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})

修改application.properties数据库配置

#db1数据库配置
db1.datasource.url = url
db1.datasource.username = username
db1.datasource.password = password
db1.datasource.driver-class-name=oracle.jdbc.OracleDriver

#db2数据库配置
db2.datasource.url = url
db2.datasource.username = username
db2.datasource.password = password
db2.datasource.driver-class-name=oracle.jdbc.OracleDriver

DataSourceConfig读取数据库配置,手动配置数据源

import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
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.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;

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

@Configuration
public class DataSourceConfig {
    /**
     * db1数据库配置
     */
    @Bean("db1")
    @ConfigurationProperties(prefix = "db1.datasource")
    public DataSource db1Source() {
        return DataSourceBuilder.create().build();
    }

    /**
     * db2数据库配置
     */
    @Bean("db2")
    @ConfigurationProperties(prefix = "db2.datasource")
    public DataSource db2Source() {
        return DataSourceBuilder.create().build();
    }

    /**
     * 动态数据库配置
     */
    @Primary
    @Bean(name = "dynamicDataSource")
    public DataSource dynamicDataSource() {
        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        // 默认数据源
        dynamicDataSource.setDefaultTargetDataSource(db1Source());

        // 配置多数据源
        Map dsMap = new HashMap(5);
        dsMap.put("db1", db1Source());
        dsMap.put("db2", db2Source());

        dynamicDataSource.setTargetDataSources(dsMap);

        return dynamicDataSource;
    }

    /**
     * 配置@Transactional注解事物
     * @return
     */
    @Bean
    public PlatformTransactionManager transactionManager() {
        return new DataSourceTransactionManager(dynamicDataSource());
    }

}

配置DataSourceContextHolder上下文

public class DataSourceContextHolder {
    // 默认数据源
    public static final String DEFAULT_DS = "db1";

    private static final ThreadLocal contextHolder = new ThreadLocal<>();

    public static void setDB(String dbType) {
        contextHolder.set(dbType);
    }

    public static String getDB() {
        return (contextHolder.get());
    }

    public static void clearDB() {
        contextHolder.remove();
    }
}

动态数据源

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

public class DynamicDataSource extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
        return DataSourceContextHolder.getDB();
    }
}

注解方式配置

自定义注解

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface DataBase {
    String value() default "db1";
}

切面

import com.xxx.common.annotation.DataBase;
import com.xxx.common.config.DataSourceContextHolder;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
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.stereotype.Component;

import java.lang.reflect.Method;

@Aspect
@Component
public class DataBaseAspect {

    @Pointcut("@annotation(com.xxx.common.annotation.DataBase)")
    public void dbPointCut() {

    }

    @Before("dbPointCut()")
    public void beforeSwitchDS(JoinPoint point){
        //获得当前访问的class
        Class className = point.getTarget().getClass();
        //获得访问的方法名
        String methodName = point.getSignature().getName();
        //得到方法的参数的类型
        Class[] argClass = ((MethodSignature)point.getSignature()).getParameterTypes();

        String dataSource = DataSourceContextHolder.DEFAULT_DS;
        try {
            // 得到访问的方法对象
            Method method = className.getMethod(methodName, argClass);

            // 判断是否存在@DateBase注解
            if (method.isAnnotationPresent(DataBase.class)) {
                DataBase annotation = method.getAnnotation(DataBase.class);
                // 取出注解中的数据源名
                dataSource = annotation.value();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        // 切换数据源
        DataSourceContextHolder.setDB(dataSource);
    }

    @After("dbPointCut()")
    public void afterSwitchDS(JoinPoint point){
        DataSourceContextHolder.clearDB();
    }
}

项目中调用

至此,SpringBoot + MyBaties 多数据源配置完成。使用时,在service层方法上调用@DataBase注解,无注解时,使用默认数据源。

    //默认(第一)数据源
    public void test(){
      return;
    }

    //默认(第一)数据源
    @DataBase("db1")
    public void test(){
      return;
    }

    //第二数据源
    @DataBase("db2")
    public void test(){
      return;
    }

你可能感兴趣的:(SpringBoot Mybatis 多数据源配置 注解方式切换数据源)