SpringBoot动态切换数据源

1、配置文件中配置多个数据库连接

# MysqL配置
spring.datasource.local.jdbc-url=jdbc:MysqL://192.168.1.115:3308/cq_njdd?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
spring.datasource.local.username=root
spring.datasource.local.password=root3308
spring.datasource.local.driver-class-name=com.MysqL.cj.jdbc.Driver

spring.datasource.remote.jdbc-url=jdbc:MysqL://192.168.1.115:8307/cq_njdd?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
spring.datasource.remote.username=root
spring.datasource.remote.password=root2022
spring.datasource.remote.driver-class-name=com.MysqL.cj.jdbc.Driver

这里需要注意如果SpringBoot版本是2.0之后的版本,需要把【url】和【driverClassName】写成【jdbc-url】和【driver-class-name】,否则会报错【jdbcUrl is required with driverClassName.】


2、数据源枚举类

自定义数据源枚举类,这里配置了两个数据源,则创建两个枚举。

public enum DataSourceType {
    REMOTE,
    LOCAL
}

3、注入数据源

生成bean。单一数据源与Mybatis整合时将DataSource数据源作为参数构建【sqlSessionFactory】,而多个数据源的话只需要将作为参数的数据源改为动态的数据源即可。

下面将配置的数据源全部注入到bean中,其中可设置认数据源。留作之后调用

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

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


@Configuration
public class DataSourceConfig {

    @Bean
    @ConfigurationProperties("spring.datasource.remote")
    public DataSource remoteDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean
    @ConfigurationProperties("spring.datasource.local")
    public DataSource localDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean(name = "dynamicDataSource")
    @Primary
    public DynamicDataSource dataSource(DataSource remoteDataSource, DataSource localDataSource) {
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put(DataSourceType.REMOTE.name(), remoteDataSource);
        targetDataSources.put(DataSourceType.LOCAL.name(), localDataSource);
        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        // 注入目标数据源,可以是多个
        dynamicDataSource.setTargetDataSources(targetDataSources);
        // 注入认数据源,只能是一个
        dynamicDataSource.setDefaultTargetDataSource(localDataSource);
        return dynamicDataSource ;
    }
}

4、切换数据源

SpringBoot动态切换数据源主要依靠AbstractRoutingDataSource类,这个抽象类中有一个属性为【targetDataSources】

 该属性为Map结构,所有需要切换的数据源都存放在其中,根据指定的KEY进行切换。当然还有一个认的数据源。

而切换数据源则需要重写该抽象类中的【determineCurrentLookupKey】抽象方法,该方法的返回值决定了需要切换的数据源key,然后根据这个key去之前的【targetDataSources】Map中取数据源。

 所以我们需要重写【determineCurrentLookupKey】方法,如下:

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


public class DynamicDataSource extends AbstractRoutingDataSource {

    @Override
    protected Object determineCurrentLookupKey() {
        return DynamicDataSourceContextHolder.getDataSourceType();
    }
}

5、切换数据源管理类

数据源属于公共资源,考虑到多线程的情况下,我们将数据源存储在【ThreadLocal】中,保证线程隔离。

public class DynamicDataSourceContextHolder {

    private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();


    public static void setDataSourceType(String dataSourceType){
        CONTEXT_HOLDER.set(dataSourceType);
    }


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

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

6、自定义多数据源切换注解

为了操作方便且低耦合,定义一个切换数据源的注解,如下:

import java.lang.annotation.*;

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {

    DataSourceType value() default DataSourceType.LOCAL;
}

7、AOP切面拦截

切面的作用就是在注解的方法执行前,取DataSource注解【value】值设置到【ThreadLocal】中,然后在方法执行后清除掉【ThreadLocal】中的key,保证如果不切换数据源时使用认的数据源。

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
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.lang.reflect.Method;

@Aspect
@Order(1)
@Component
public class DataSourceAspect {

    @pointcut("@annotation(com.bw.note.config.datasource.DataSource)")
    public void dspointcut() {
    }

    @Around("dspointcut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getmethod();
        DataSource dataSource = method.getAnnotation(DataSource.class);
        if (dataSource != null) {
            DynamicDataSourceContextHolder.setDataSourceType(dataSource.value().name());
        }
        try {
            return point.proceed();
        } finally {

            DynamicDataSourceContextHolder.clearDataSourceType();
        }
    }
}

8、切换数据源注解实际使用

直接在实现方法上使用自定义的注解即可。


 9、SpringBoot启动类

启动类的【SpringBootApplication】注解加上【exclude = DataSourceAutoConfiguration.class】属性

@SpringBootApplication(scanBasePackages = "com.bw",exclude = DataSourceAutoConfiguration.class)
@MapperScan("com.bw.note.mapper")
public class NoteServiceApplication {

    public static void main(String[] args) {
        SpringApplication.run(NoteServiceApplication.class, args);
    }

}

参考资料

https://blog.csdn.net/qq_36997144/article/details/123439244

相关文章

显卡天梯图2024最新版,显卡是电脑进行图形处理的重要设备,...
初始化电脑时出现问题怎么办,可以使用win系统的安装介质,连...
todesk远程开机怎么设置,两台电脑要在同一局域网内,然后需...
油猴谷歌插件怎么安装,可以通过谷歌应用商店进行安装,需要...
虚拟内存这个名词想必很多人都听说过,我们在使用电脑的时候...