SpringBoot配置多数据源

配置多数据源的方式有很多,这里介绍一种我在工作中验证可行的方式。

大致的依赖:
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
    <version>2.1.9.RELEASE</version>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.2.1</version>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.2.1</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.13</version>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.51</version>
</dependency>
<!-- 注意mybatis-plus的版本一定要适配springboot,否则在运行中可能会有问题 -->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.1.0</version>
</dependency>
<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator-framework</artifactId>
    <version>4.2.0</version>
</dependency>
<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator-recipes</artifactId>
    <version>4.2.0</version>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
</dependency>
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjrt</artifactId>
    <version>1.9.4</version>
</dependency>

application.yml的数据源部分的配置如下,此处整合了druid:

spring:
  datasource:
    druid:
      DB1:
        url: jdbc:mysql://127.0.0.1:3306/db1?autoReconnect=true&useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&zeroDateTimeBehavior=convertToNull
        username: root
        password: root
        validationQuery: select 1
        maxActive: 30
        minIdle: 4
        initialSize: 4
        testOnReturn: false
        testWhileIdle: true
        testOnBorrow: true
        logAbandoned: false
        timeBetweenEvictionRunsMillis: 600000
        minEvictableIdleTimeMillis: 600000
        poolPreparedStatements: true
        max-open-prepared-statements: 20
        removeAbandoned: true
        driver-class-name: com.mysql.cj.jdbc.Driver

      DB2:
        url: jdbc:mysql://127.0.0.1:3306/db2?autoReconnect=true&useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&zeroDateTimeBehavior=convertToNull
        username: root
        password: root
        validationQuery: select 1
        maxActive: 30
        minIdle: 4
        initialSize: 4
        testOnReturn: false
        testWhileIdle: true
        testOnBorrow: true
        logAbandoned: false
        timeBetweenEvictionRunsMillis: 600000
        minEvictableIdleTimeMillis: 600000
        poolPreparedStatements: true
        max-open-prepared-statements: 20
        removeAbandoned: true
        driver-class-name: com.mysql.cj.jdbc.Driver

随后我们需要写一些配置类,进行配置。创建一个config包,在包内创建如下配置类:

/**
 * 数据源名称
 */
public interface DataSourceName {

    String DB1= "DB1";

    String DB2= "DB2";
}
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * DataSource 注解 标记数据源
 *
 * 默认数据库 Master
 */
@Documented
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface DataSource {
    String value() default DataSourceName.DRDS;
}
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

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

public class DynamicDataSource extends AbstractRoutingDataSource {

    private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();
    /**
     * 配置DataSource, defaultTargetDataSource为主数据库
     */
    public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources) {
        super.setDefaultTargetDataSource(defaultTargetDataSource);
        super.setTargetDataSources(targetDataSources);
        super.afterPropertiesSet();
    }

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

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

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

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

}
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Qualifier;
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 javax.sql.DataSource;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;

/**
 * 多数据源配置
 *
 */
@Configuration
public class DataSourceConfig {

    private static final Logger logger = LoggerFactory.getLogger(DataSourceConfig.class);

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

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

    @Bean
    @Primary
    public DynamicDataSource dataSource(@Qualifier("db1DataSource") DataSource db1DataSource,
                                        @Qualifier("db2DataSource") DataSource db2DataSource) throws SQLException {
        Map<Object, Object> targetDataSources = new HashMap<>(2);
        targetDataSources.put(DataSourceName.DB1, db1DataSource);
        targetDataSources.put(DataSourceName.DB2, db2DataSource);
        // 还有数据源,在targetDataSources中继续添加
        logger.info("MultiDataSources:" + targetDataSources);
        return new DynamicDataSource(db1DataSource, targetDataSources);
    }
}
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**
 * 数据源切点配置
 *
 */
@Aspect
@Component
public class DataSourceAspect implements Ordered {

    protected Logger logger = LoggerFactory.getLogger(getClass());

    /**
     * 切点: 所有配置 DataSource 注解的方法
     */
    @Pointcut("@annotation(com.xx.xx.xx.config.DataSource)")
    public void dataSourcePointCut() {}

    @Around("dataSourcePointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        DataSource ds = method.getAnnotation(DataSource.class);
        // 通过判断 DataSource 中的值来判断当前方法应用哪个数据源
        DynamicDataSource.setDataSource(ds.value());
        logger.debug("set datasource is {} ", ds.value());
        try {
            return point.proceed();
        } finally {
            DynamicDataSource.clearDataSource();
            logger.debug("clean datasource");
        }
    }

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

经过以上操作,多数据源的配置基本完成,使用时,如果是主数据源(即:默认数据源),不需要任何进一步的操作,直接写代码即可;如果要切换到非默认数据源,只需要在接口上添加如下注解:

@DataSource(DataSourceName.DB2)

注:由于本案例使用了durid的数据源,可能在运行时会有报错,此时,在config包下,再添加一个新的配置类:

import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.sql.DataSource;

@Configuration
public class DruidConfig {
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.druid")
    public DataSource getDataSource() {
        return new DruidDataSource();
    }
}

版权声明:本文为zuodaye原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。