Spring Boot中动态数据源切换

Spring Boot中动态数据源切换

运行版本

  • spring boot 2.2.5.RELEASE
  • 数据源使用spring boot自带的Hikari
  • 数据库连接 mybatis plus (spring data jpa也可行)
  • 不支持事务

步骤说明:

1.新建spring boot项目引入依赖

        
        
            org.springframework.boot
            spring-boot-starter-web
        
        
        
        
            com.baomidou
            mybatis-plus-boot-starter
            3.1.0
        
        
        
        
            mysql
            mysql-connector-java
        
        
        
        
            org.aspectj
            aspectjrt
            1.7.4
        
        
            org.aspectj
            aspectjweaver
            1.7.4
        
        
        
        
        
            org.springframework.boot
            spring-boot-configuration-processor
            true
        

2.修改application.yaml配置文件

server:
  port: 9099
spring:
  application:
    name: datasource-switch
  datasource:
    one:
      driver-class-name: com.mysql.cj.jdbc.Driver
      jdbc-url: jdbc:mysql://10.172.246.234:3306/zhouyf?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai
      username: root
      password: 123456
    two:
      driver-class-name: com.mysql.cj.jdbc.Driver
      jdbc-url: jdbc:mysql://10.172.246.234:3306/zhouyf2?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai
      username: root
      password: 123456
    three:
      driver-class-name: com.mysql.cj.jdbc.Driver
      jdbc-url: jdbc:mysql://10.172.246.234:3306/zhouyf3?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai
      username: root
      password: 123456
#读取mybatis的mapper文件
mybatis-plus:
  mapper-locations: classpath:mapper/*.xml

3.创建数据源类,读取配置文件中配置的数据源

@Component
@Data
@ConfigurationProperties(prefix ="spring.datasource")
@EnableConfigurationProperties
public class DBproperties {

    private HikariDataSource one;

    private HikariDataSource two;

    private HikariDataSource three;
    
}

HikariDataSource 中的HikariConfig为数据源可配置的信息,如有需要可以参考配置在配置文件中

4.创建DynamicDataSource类扩展Spring的AbstractRoutingDataSource抽象类,重写 determineCurrentLookupKey() 方法

/**
 * DynamicDataSource扩展Spring的AbstractRoutingDataSource抽象类,重写 determineCurrentLookupKey() 方法
 */
public class DynamicDataSource extends AbstractRoutingDataSource {

    /**
     * ThreadLocal 用于提供线程局部变量,在多线程环境可以保证各个线程里的变量独立于其它线程里的变量。
     * 也就是说 ThreadLocal 可以为每个线程创建一个【单独的变量副本】,相当于线程的 private static 类型变量。
     */
    private static final ThreadLocal CONTEXT_HOLDER = new ThreadLocal<>();

    /**
     * 决定使用哪个数据源之前需要把多个数据源的信息以及默认数据源信息配置好
     *  @param defaultTargetDataSource 默认数据源
     * @param targetDataSources       目标数据源
     */
    public DynamicDataSource(DataSource defaultTargetDataSource, Map targetDataSources) {
        super.setDefaultTargetDataSource(defaultTargetDataSource);
        super.setTargetDataSources(targetDataSources);
        super.afterPropertiesSet();
    }


    @Override
    protected Object determineCurrentLookupKey() {
        return getDataSource();
    }

    public static void setDataSource(String dataSource) {
        CONTEXT_HOLDER.set(dataSource);
    }

    public static String getDataSource() {
        return CONTEXT_HOLDER.get();
    }

    public static void clearDataSource() {
        CONTEXT_HOLDER.remove();
    }
    
}

4.注册刚创建的实体DynamicDataSource并把数据源信息传进去

@Configuration
public class DynamicDataSourceConfig {

    @Autowired
    private DBproperties properties;

    @Bean
    @Primary
    public DynamicDataSource dataSource() {
        Map targetDataSources =new HashMap<>();
        targetDataSources.put("one", properties.getOne());
        targetDataSources.put("two", properties.getTwo());
        targetDataSources.put("three", properties.getThree());
        //设置one为默认数据源
        return new DynamicDataSource(properties.getOne(),targetDataSources);
    }

}

5.创建注解@UseDataSource并创建该注解的切面

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


@Slf4j
@Aspect
@Component
public class DataSourceAspect implements Ordered {
    @Pointcut("@annotation(zhouyf.datasource.UseDataSource)")
    public void dataSourcePointCut() {}

    @Around("dataSourcePointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        UseDataSource ds = method.getAnnotation(UseDataSource.class);
        if (ds == null) {
            //value为空则使用默认数据源
            DynamicDataSource.setDataSource("one");
            log.warn("----数据源使用----- 当前数据源为:one");
        } else {
            DynamicDataSource.setDataSource(ds.value());
            log.warn("----数据源使用----- 当前数据源为:"+ds.value());
        }
        try {
            return point.proceed();
        } finally {
            DynamicDataSource.clearDataSource();
        }
    }

    @Override
    public int getOrder() {
        return 1;
    }
}

6.测试

Spring Boot中动态数据源切换_第1张图片
数据库信息以及其中的数据

Spring Boot中动态数据源切换_第2张图片
image

Spring Boot中动态数据源切换_第3张图片
image

Spring Boot中动态数据源切换_第4张图片
image

spring boot data jpa 使用注意事项

  • 如果 jpa.hibernate.ddl-auto为update或great则会在默认的数据源上创建或修改表结构
  • 可以返回同一实体类(如果数据源2和数据源1同时都有一张表的情况下,会去切换的数据源下找)

你可能感兴趣的:(Spring Boot中动态数据源切换)