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.】

 数据库连接池配置

spring.datasource.type=com.zaxxer.hikari.HikariDataSource
spring.datasource.hikari.minimum-idle=10
# 连接池中允许的最大连接数
spring.datasource.hikari.maximum-pool-size=200
# 自动提交从池中返回的连接
spring.datasource.hikari.auto-commit=true
# 连接允许在池中闲置的最长时间,超时则被释放(retired)
spring.datasource.hikari.idle-timeout=30000
spring.datasource.hikari.pool-name=ServiceTaskHikariCP
# 一个连接的生命时长(毫秒),超时而且没被使用则被释放(retired),缺省:30分钟,建议设置比数据库超时时长少30秒,参考MySQLwait_timeout参数(show variables like '%timeout%';)
spring.datasource.hikari.max-lifetime=1200000
# 等待连接池分配连接的最大时长(毫秒),超过这个时长还没可用的连接则发生SQLException
spring.datasource.hikari.connection-timeout=30000

# 验证连接是否有效。此参数必须设置为非空字符串,下面三项设置成true才能生效
spring.datasource.dbcp2.validation-query=select 1
# 指明是否在从池中取出连接前进行检验,如果检验失败,则从池中去除连接并尝试取出另一个
spring.datasource.dbcp2.test-on-borrow=true
# 指明是否在归还到池中前进行检验
spring.datasource.dbcp2.test-on-return=false
# 验证连接的有效性
spring.datasource.dbcp2.test-while-idle=true
# 空闲连接回收的时间间隔,与test-while-idle一起使用,设置5分钟
spring.datasource.dbcp2.time-between-eviction-runs-millis=300000
# 连接池空闲连接的有效时间 ,设置30分钟
spring.datasource.dbcp2.soft-min-evictable-idle-time-millis=1800000

 不加配置的话会报No operations allowed after connection closed.异常。


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

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐