概述

Spring Boot 应用通常将数据存储在单个关系型数据库中,但有时我们需要访问多个数据库。本文将介绍如何在 Spring Boot 中配置和使用多数据源。

如果需要了解单数据源配置,可以参考我们的 Spring Data JPA 入门教程

默认行为

先回顾下在 application.yml 中声明单个数据源的标准配置:

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/db1
    username: user
    password: pass
    driverClassName: com.mysql.cj.jdbc.Driver

Spring 内部会将这些配置映射到 org.springframework.boot.autoconfigure.jdbc.DataSourceProperties 实例。看看这个类的实现:

@ConfigurationProperties(prefix = "spring.datasource")
public class DataSourceProperties implements BeanClassLoaderAware, InitializingBean {

    // ...

    /**
     * JDBC 驱动的完整类名。默认根据 URL 自动检测。
     */
    private String driverClassName;

    /**
     * 数据库的 JDBC URL。
     */
    private String url;

    /**
     * 数据库登录用户名。
     */
    private String username;

    /**
     * 数据库登录密码。
     */
    private String password;

    // ...

}

关键点在于 @ConfigurationProperties 注解,它能自动将配置属性映射到 Java 对象。

扩展默认配置

要使用多数据源,我们需要在 Spring 应用上下文中声明多个具有不同映射的数据源 Bean。可以通过配置类实现:

@Configuration
public class TodoDatasourceConfiguration {

    @Bean
    @ConfigurationProperties("spring.datasource.todos")
    public DataSourceProperties todosDataSourceProperties() {
        return new DataSourceProperties();
    }
}

@Configuration
public class TopicDatasourceConfiguration {

    @Bean
    @ConfigurationProperties("spring.datasource.topics")
    public DataSourceProperties topicsDataSourceProperties() {
        return new DataSourceProperties();
    }
}

对应的 application.yml 配置应调整为:

spring:
  datasource:
    todos:
      url: jdbc:mysql://localhost:3306/todos_db
      username: todo_user
      password: todo_pass
      driverClassName: com.mysql.cj.jdbc.Driver
    topics:
      url: jdbc:mysql://localhost:3306/topics_db
      username: topic_user
      password: topic_pass
      driverClassName: com.mysql.cj.jdbc.Driver

然后基于 DataSourceProperties 创建数据源:

@Bean
public DataSource todosDataSource() {
    return todosDataSourceProperties()
      .initializeDataSourceBuilder()
      .build();
}

@Bean
public DataSource topicsDataSource() {
    return topicsDataSourceProperties()
      .initializeDataSourceBuilder()
      .build();
}

Spring Data JDBC

使用 Spring Data JDBC 时,需要为每个 DataSource 配置一个 JdbcTemplate

@Bean
public JdbcTemplate todosJdbcTemplate(@Qualifier("todosDataSource") DataSource dataSource) {
    return new JdbcTemplate(dataSource);
}

@Bean
public JdbcTemplate topicsJdbcTemplate(@Qualifier("topicsDataSource") DataSource dataSource) {
    return new JdbcTemplate(dataSource);
}

使用时通过 @Qualifier 指定注入哪个模板:

@Autowired
@Qualifier("topicsJdbcTemplate")
JdbcTemplate jdbcTemplate;

Spring Data JPA

使用 Spring Data JPA 时,我们通常需要这样的仓库接口(Todo 是实体类):

public interface TodoRepository extends JpaRepository<Todo, Long> {}

因此需要为每个数据源声明 EntityManager 工厂:

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
  basePackageClasses = Todo.class,
  entityManagerFactoryRef = "todosEntityManagerFactory",
  transactionManagerRef = "todosTransactionManager"
)
public class TodoJpaConfiguration {

    @Bean
    public LocalContainerEntityManagerFactoryBean todosEntityManagerFactory(
      @Qualifier("todosDataSource") DataSource dataSource,
      EntityManagerFactoryBuilder builder) {
        return builder
          .dataSource(dataSource)
          .packages(Todo.class)
          .build();
    }

    @Bean
    public PlatformTransactionManager todosTransactionManager(
      @Qualifier("todosEntityManagerFactory") LocalContainerEntityManagerFactoryBean todosEntityManagerFactory) {
        return new JpaTransactionManager(Objects.requireNonNull(todosEntityManagerFactory.getObject()));
    }
}

⚠️ 需要注意的限制

  1. 必须拆分包路径,为每个数据源单独配置 @EnableJpaRepositories
  2. 要注入 EntityManagerFactoryBuilder,必须将某个数据源声明为 @Primary(因为 JpaBaseConfiguration 需要单个数据源注入)
  3. 框架某些部分可能不期望存在多数据源配置,容易踩坑

配置 Hikari 连接池

如果要配置 Hikari 连接池,只需在数据源定义上添加 @ConfigurationProperties

@Bean
@ConfigurationProperties("spring.datasource.todos.hikari")
public DataSource todosDataSource() {
    return todosDataSourceProperties()
      .initializeDataSourceBuilder()
      .build();
}

然后在 application.properties 中添加 Hikari 特定配置:

spring.datasource.todos.hikari.connectionTimeout=30000 
spring.datasource.todos.hikari.idleTimeout=600000 
spring.datasource.todos.hikari.maxLifetime=1800000 

总结

本文介绍了在 Spring Boot 中配置多数据源的完整流程。虽然需要额外配置且可能遇到一些坑,但最终是可行的。关键点包括:

✅ 通过 @ConfigurationProperties 分离数据源配置
✅ 为每个数据源创建独立的 DataSource Bean
✅ JDBC 场景需配置多个 JdbcTemplate
✅ JPA 场景需配置独立的 EntityManagerFactory 和事务管理器
✅ Hikari 连接池可通过属性前缀配置

完整代码示例可在 GitHub 获取。


原始标题:Configure and Use Multiple DataSources in Spring Boot