5.多数据源配置与使用

虽然我们前面的db系列教程更多的是基于单数据源的db操作,但是实际的业务开发中,难免会遇到一个项目配置多个数据源的情况,接下来本文将介绍一下多个数据源可以怎么配置,我们的JdbcTemplate又应该如何获取

I. 环境准备

1. 数据库相关

以mysql为例进行演示说明,因为需要多数据源,一个最简单的case就是一个物理库上多个逻辑库,本文是基于本机的mysql进行操作

创建数据库teststory,两个库下都存在一个表money (同名同结构表,但是数据不同哦)

CREATE TABLE `money` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(20) NOT NULL DEFAULT '' COMMENT '用户名',
  `money` int(26) NOT NULL DEFAULT '0' COMMENT '钱',
  `is_deleted` tinyint(1) NOT NULL DEFAULT '0',
  `create_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`),
  KEY `name` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;

data

2. 项目环境

本项目借助SpringBoot 2.2.1.RELEASE + maven 3.5.3 + IDEA进行开发

下面是核心的pom.xml(源码可以再文末获取)

<dependencies>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-jdbc</artifactId>
    </dependency>
</dependencies>

配置文件信息application.yml

# 数据库相关配置
spring:
  datasource:
    story:
      driver-class-name: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql://127.0.0.1:3306/story?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai
      username: root
      password:
    test:
      driver-class-name: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai
      username: root
      password:


# 日志相关
logging:
  level:
    root: info
    org:
      springframework:
        jdbc:
          core: debug

请注意上面的数据库配置,我们前面介绍的但数据库配置如下,它们层级并不一样,上面的配置需要我们自己额外进行加载解析

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/story?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai
    username: root
    password:

II. 多数据源支持

1. 数据源配置

SpringBoot帮我们省略了很多操作,单数据源时,在yaml文件中配置数据库相关信息之后,我们不需要任何其他操作,Spring会帮我们实例对应的DataSource,然后借助它来创建JdbcTemplate

而多数据源则需要我们自己来额外处理了,请注意上面的配置信息,和默认的配置没什么差别,只是前缀多了一层,我们可以借助@ConfigurationProperties来加载配置文件

@Configuration
public class DataSourceConfiguration {

    @Primary
    @Bean(name = "storyDataSourceProperties")
    @ConfigurationProperties(prefix = "spring.datasource.story")
    public DataSourceProperties storyDataSourceProperties() {
        return new DataSourceProperties();
    }

    @Primary
    @Bean(name = "storyDataSource")
    public DataSource storyDataSource(@Qualifier("storyDataSourceProperties") DataSourceProperties storyDataSourceProperties) {
        return storyDataSourceProperties.initializeDataSourceBuilder().build();
    }

    @Bean(name = "testDataSourceProperties")
    @ConfigurationProperties(prefix = "spring.datasource.test")
    public DataSourceProperties testDataSourceProperties() {
        return new DataSourceProperties();
    }

    @Bean(name = "testDataSource")
    public DataSource testDataSource(@Qualifier("testDataSourceProperties") DataSourceProperties testDataSourceProperties) {
        return testDataSourceProperties.initializeDataSourceBuilder().build();
    }
}

配置代码如上,一个是将数据源配置映射为bean DataSourceProperties, 一个是根据数据源配置实例化DataSource

此外两个@Primary注解,主要是为了设置默认的数据源

2. JdbcTemplate实例化

数据源已经获取到之后,再实例化JdbcTemplate就很简单了,除了直接声明bean之外,也可以基于DataSource来手动创建临时对象,下面给出两种使用姿势

定义对应的bean,对于业务使用更友好

@Bean("storyJdbcTemplate")
public JdbcTemplate storyJdbcTemplate(@Qualifier("storyDataSource") DataSource storyDataSource) {
    return new JdbcTemplate(storyDataSource);
}

@Bean("testJdbcTemplate")
public JdbcTemplate testJdbcTemplate(@Qualifier("testDataSource") DataSource testDataSource) {
    return new JdbcTemplate(testDataSource);
}

借助ApplicationContext来实例化JdbcTemplate,使用更灵活

@Service
public class JdbcServer implements ApplicationContextAware {

    private JdbcTemplate storyJdbcTemplate;

    private JdbcTemplate testJdbcTemplate;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        Map<String, DataSource> map = applicationContext.getBeansOfType(DataSource.class);
        System.out.println(map);
        storyJdbcTemplate = new JdbcTemplate(map.get("storyDataSource"));
        testJdbcTemplate = new JdbcTemplate(map.get("testDataSource"));
    }
}

3. 测试case

最后简单测试一下上面创建的两个JdbcTemplate是否访问不同的数据库

public void query() {
    List<Map<String, Object>> storyRes = storyJdbcTemplate.queryForList("select * from money where id in (1, 1000)");
    List<Map<String, Object>> testRes = testJdbcTemplate.queryForList("select * from money where id in (1, 1000)");
    System.out.println(storyRes);
    System.out.println("--------------");
    System.out.println(testRes);
}

启动类如下

@SpringBootApplication
public class Application {

    public Application(JdbcServer jdbcServer) {
        jdbcServer.query();
    }

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

testcase

相同的sql,获取的结果并不一样,分别从两个库中获取的数据

4. 小结

使用多数据源,比较简单的思路就是自定义配置的加载方式,获取不同的DataSourceProperties对象,然后基于它来创建DataSource实例

利用JdbcTemplate来操作db的应用场景,直接选择不同的数据源Datasource就行了

当然实际的项目中,我们一般会借助HerbernateMyabtisJooq等orm框架,那么使用orm时,多数据源又应该怎么处理呢? (请持续关注,相关博文即将上线)

II. 其他

0. 项目

系列博文

项目源码