多数据源配置

多数据源配置

先在 Spring Boot 的配置文件 application.properties 中设置两个你要链接的数据库配置,比如这样:

spring.datasource.primary.jdbc-url=jdbc:mysql://localhost:3306/test1
spring.datasource.primary.username=root
spring.datasource.primary.password=123456
spring.datasource.primary.driver-class-name=com.mysql.cj.jdbc.Driver

spring.datasource.secondary.jdbc-url=jdbc:mysql://localhost:3306/test2
spring.datasource.secondary.username=root
spring.datasource.secondary.password=123456
spring.datasource.secondary.driver-class-name=com.mysql.cj.jdbc.Driver

多数据源配置的时候,与单数据源不同点在于 spring.datasource 之后多设置一个数据源名称 primary 和 secondary 来区分不同的数据源配置,这个前缀将在后续初始化数据源的时候用到。数据源连接配置 2.x 和 1.x 的配置项是有区别的:2.x 使用 spring.datasource.secondary.jdbc-url,而 1.x 版本使用 spring.datasource.secondary.url。如果你在配置的时候发生了这个报错 java.lang.IllegalArgumentException: jdbcUrl is required with driverClassName.,那么就是这个配置项的问题。

完成多数据源的配置信息之后,就来创建个配置类来加载这些配置信息,初始化数据源,以及初始化每个数据源要用的 JdbcTemplate。你只需要在你的 Spring Boot 应用下添加下面的这个配置类即可完成!

@Configuration
public class DataSourceConfiguration {

    @Primary
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.primary")
    public DataSource primaryDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.secondary")
    public DataSource secondaryDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean
    public JdbcTemplate primaryJdbcTemplate(@Qualifier("primaryDataSource") DataSource primaryDataSource) {
        return new JdbcTemplate(primaryDataSource);
    }

    @Bean
    public JdbcTemplate secondaryJdbcTemplate(@Qualifier("secondaryDataSource") DataSource secondaryDataSource) {
        return new JdbcTemplate(secondaryDataSource);
    }

}

前两个 Bean 是数据源的创建,通过@ConfigurationProperties 可以知道这两个数据源分别加载了 spring.datasource.primary.*spring.datasource.secondary.* 的配置。 @Primary 注解指定了主数据源,就是当我们不特别指定哪个数据源的时候,就会使用这个 Bean 后两个 Bean 是每个数据源对应的 JdbcTemplate。可以看到这两个 JdbcTemplate 创建的时候,分别注入了 primaryDataSource 数据源和 secondaryDataSource 数据源。

@RunWith(SpringRunner.class)
@SpringBootTest
public class Chapter37ApplicationTests {

    @Autowired
    protected JdbcTemplate primaryJdbcTemplate;

    @Autowired
    protected JdbcTemplate secondaryJdbcTemplate;

    @Before
    public void setUp() {
        primaryJdbcTemplate.update("DELETE  FROM  USER ");
        secondaryJdbcTemplate.update("DELETE  FROM  USER ");
    }

    @Test
    public void test() throws Exception {
        // 往第一个数据源中插入 2 条数据
        primaryJdbcTemplate.update("insert into user(name,age) values(?, ?)", "aaa", 20);
        primaryJdbcTemplate.update("insert into user(name,age) values(?, ?)", "bbb", 30);

        // 往第二个数据源中插入 1 条数据,若插入的是第一个数据源,则会主键冲突报错
        secondaryJdbcTemplate.update("insert into user(name,age) values(?, ?)", "ccc", 20);

        // 查一下第一个数据源中是否有 2 条数据,验证插入是否成功
        Assert.assertEquals("2", primaryJdbcTemplate.queryForObject("select count(1) from user", String.class));

        // 查一下第一个数据源中是否有 1 条数据,验证插入是否成功
        Assert.assertEquals("1", secondaryJdbcTemplate.queryForObject("select count(1) from user", String.class));
    }

}

dynamic-datasource-spring-boot-starter

dynamic-datasource-spring-boot-starter 是一个基于 springboot 的快速集成多数据源的启动器。

  1. 支持 数据源分组 ,适用于多种场景 纯粹多库 读写分离 一主多从 混合模式。
  2. 支持数据库敏感配置信息 加密 ENC()。
  3. 支持每个数据库独立初始化表结构 schema 和数据库 database。
  4. 支持 自定义注解 ,需继承 DS(3.2.0+)。
  5. 提供对 Druid,Mybatis-Plus,P6sy,Jndi 的快速集成。
  6. 简化 Druid 和 HikariCp 配置,提供 全局参数配置 。配置一次,全局通用。
  7. 提供 自定义数据源来源 方案。
  8. 提供项目启动后 动态增加移除数据源 方案。
  9. 提供 Mybatis 环境下的 纯读写分离 方案。
  10. 提供使用 spel 动态参数 解析数据源方案。内置 spel,session,header,支持自定义。
  11. 支持 多层数据源嵌套切换 。(ServiceA »> ServiceB »> ServiceC)。
  12. 提供对 shiro,sharding-jdbc,quartz 等第三方库集成的方案,注意事项和示例。
  13. 提供 基于 seata 的分布式事务方案。 附:不支持原生 spring 事务。
  14. 提供 本地多数据源事务方案。 附:不支持原生 spring 事务。

基础配置

<dependency>
  <groupId>com.baomidou</groupId>
  <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
  <version>${version}</version>
</dependency>

从 2.0.0 开始所有数据源的配置同级,不再有默认的主从限制,你可以给你的数据源起任何合适的名字。

spring:
  datasource:
    dynamic:
      primary: master #设置默认的数据源或者数据源组,默认值即为master
      strict: false #设置严格模式,默认false不启动. 启动后在未匹配到指定数据源时候会抛出异常,不启动则使用默认数据源.
      datasource:
        master:
          url: jdbc:mysql://xx.xx.xx.xx:3306/dynamic
          username: root
          password: 123456
          driver-class-name: com.mysql.jdbc.Driver # 3.2.0开始支持SPI可省略此配置
        slave_1:
          url: jdbc:mysql://xx.xx.xx.xx:3307/dynamic
          username: root
          password: 123456
          driver-class-name: com.mysql.jdbc.Driver
        slave_2:
          url: ENC(xxxxx) # 内置加密,使用请查看详细文档
          username: ENC(xxxxx)
          password: ENC(xxxxx)
          driver-class-name: com.mysql.jdbc.Driver
          schema: db/schema.sql # 配置则生效,自动初始化表结构
          data: db/data.sql # 配置则生效,自动初始化数据
          continue-on-error: true # 默认true,初始化失败是否继续
          separator: ";" # sql默认分号分隔符


        #......省略
        #以上会配置一个默认库master,一个组slave下有两个子库slave_1,slave_2

多主多从方案:(谨慎使用,你清楚的知道多个主库间需要自己做同步)

# 多主多从                      纯粹多库(记得设置primary)                   混合配置
spring:                               spring:                               spring:
  datasource:                           datasource:                           datasource:
    dynamic:                              dynamic:                              dynamic:
      datasource:                           datasource:                           datasource:
        master_1:                             mysql:                                master:
        master_2:                             oracle:                               slave_1:
        slave_1:                              sqlserver:                            slave_2:
        slave_2:                              postgresql:                           oracle_1:
        slave_3:                              h2:                                   oracle_2:

@DS 可以注解在方法上或类上,同时存在就近原则方法上注解优先于类上注解:

@Service
@DS("slave")
public class UserServiceImpl implements UserService {

  @Autowired
  private JdbcTemplate jdbcTemplate;

  public List selectAll() {
    return  jdbcTemplate.queryForList("select * from user");
  }

  @Override
  @DS("slave_1")
  public List selectByCondition() {
    return  jdbcTemplate.queryForList("select * from user where age >10");
  }
}

集成 Druid

SpringBoot2.x 默认使用 HikariCP,但在国内 Druid 的使用者非常庞大,此项目特地对其进行了适配,完成多数据源下使用 Druid 进行监控。主从可以使用不同的数据库连接池,如 master 使用 Druid 监控,从库使用 HikariCP。如果不配置连接池 type 类型,默认是 Druid 优先于 HikariCP。首先引入 druid-spring-boot-starter 依赖:

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.1.10</version>
</dependency>

然后排除原生 Druid 的快速配置类,DruidDataSourceAutoConfigure 会注入一个 DataSourceWrapper,其会在原生的 spring.datasource 下找 url,username,password 等。而我们动态数据源的配置路径是变化的。

@SpringBootApplication(exclude = DruidDataSourceAutoConfigure.class)
public class Application {

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

}

如果遇到 DruidDataSourceAutoConfigure 抛出 no suitable driver 表示注解排除没有生效尝试以下这种排除方法:

spring:
  autoconfigure:
    exclude: com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure

其他属性依旧如原生 druid-spring-boot-starter 的配置:

spring:
  datasource:
    druid:
      stat-view-servlet:
        loginUsername: admin
        loginPassword: 123456
    dynamic:
      druid: # 2.2.3开始提供全局druid参数,以下是默认值和druid原生保持一致
        initial-size: 0
        max-active: 8
        min-idle: 2
        max-wait: -1
        min-evictable-idle-time-millis: 30000
        max-evictable-idle-time-millis: 30000
        time-between-eviction-runs-millis: 0
        validation-query: select 1
        validation-query-timeout: -1
        test-on-borrow: false
        test-on-return: false
        test-while-idle: true
        pool-prepared-statements: true
        max-open-prepared-statements: 100
        filters: stat,wall
        share-prepared-statements: true
      datasource:
        master:
          username: root
          password: 123456
          driver-class-name: com.mysql.jdbc.Driver
          url: jdbc:mysql://47.100.20.186:3306/dynamic?characterEncoding=utf8&useSSL=false
          druid: # 以下参数针对每个库可以重新设置druid参数
            initial-size:
            max-active:
            min-idle:
            max-wait:
            min-evictable-idle-time-millis:
            max-evictable-idle-time-millis:
            time-between-eviction-runs-millis:
            validation-query: select 1 FROM DUAL #比如oracle就需要重新设置这个
            validation-query-timeout:
            test-on-borrow:
            test-on-return:
            test-while-idle:
            pool-prepared-statements:
            max-open-prepared-statements:
            filters:
            share-prepared-statements: