基于本文回答

播面 播面

文图音视,全方位拆解八股文
0
评论

Spring Boot 如何配置多数据源?

知识点图片

Spring Boot 默认只配置一个数据源。要配置多数据源(例如一个主库、一个从库,或者连接不同业务的数据库),通常有两种主流方案:

  1. 原生手动配置(分包隔离):通过配置类手动创建多个 DataSource,并根据包路径(Package)将不同的 Mapper/Repository 映射到不同的数据源。
  2. 使用第三方组件(动态数据源):使用如 dynamic-datasource-spring-boot-starter,通过注解切换数据源。

方案一:原生手动配置(以 MyBatis 为例)

这是最标准的方式,原理是将不同的 Mapper 接口放在不同的包下,每个包使用独立的 SqlSessionFactoryDataSource

1. 配置文件 (application.yml)

我们需要禁用默认的数据源自动配置,并定义两组数据源配置。

yaml
spring:
  datasource:
    # 主数据源配置
    primary:
      driver-class-name: com.mysql.cj.jdbc.Driver
      jdbc-url: jdbc:mysql://localhost:3306/db_primary
      username: root
      password: password
    # 第二数据源配置
    secondary:
      driver-class-name: com.mysql.cj.jdbc.Driver
      jdbc-url: jdbc:mysql://localhost:3306/db_secondary
      username: root
      password: password

# MyBatis Mapper 路径配置 (如果使用 XML)
mybatis:
  mapper-locations: classpath:mapper/**/*.xml

2. 配置数据源 Bean

创建一个配置类,读取配置文件并生成 DataSource 对象。

java
@Configuration
public class DataSourceConfig {

    @Primary // 必须指定一个主数据源,供 Spring 上下文默认使用
    @Bean(name = "primaryDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.primary")
    public DataSource primaryDataSource() {
        return DataSourceBuilder.create().build();
    }

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

3. 配置 MyBatis (分包扫描)

这是核心步骤。你需要为每个数据源创建一个配置类,指定该数据源负责扫描哪个包下的 Mapper。

主数据源配置类 (PrimaryMyBatisConfig.java)

java
@Configuration
@MapperScan(
    basePackages = "com.example.project.mapper.primary", // 扫描主库 Mapper 的包路径
    sqlSessionFactoryRef = "primarySqlSessionFactory"
)
public class PrimaryMyBatisConfig {

    @Resource(name = "primaryDataSource")
    private DataSource primaryDataSource;

    @Primary
    @Bean(name = "primarySqlSessionFactory")
    public SqlSessionFactory primarySqlSessionFactory() throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(primaryDataSource);
        // 如果有 XML 映射文件,需要在这里指定
        // bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/primary/*.xml"));
        return bean.getObject();
    }

    @Primary
    @Bean(name = "primaryTransactionManager")
    public DataSourceTransactionManager primaryTransactionManager() {
        return new DataSourceTransactionManager(primaryDataSource);
    }
}

第二数据源配置类 (SecondaryMyBatisConfig.java)

java
@Configuration
@MapperScan(
    basePackages = "com.example.project.mapper.secondary", // 扫描从库 Mapper 的包路径
    sqlSessionFactoryRef = "secondarySqlSessionFactory"
)
public class SecondaryMyBatisConfig {

    @Resource(name = "secondaryDataSource")
    private DataSource secondaryDataSource;

    @Bean(name = "secondarySqlSessionFactory")
    public SqlSessionFactory secondarySqlSessionFactory() throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(secondaryDataSource);
        return bean.getObject();
    }

    @Bean(name = "secondaryTransactionManager")
    public DataSourceTransactionManager secondaryTransactionManager() {
        return new DataSourceTransactionManager(secondaryDataSource);
    }
}

4. 使用

  • 将操作主库的 Mapper 接口放在 com.example.project.mapper.primary 包下。
  • 将操作从库的 Mapper 接口放在 com.example.project.mapper.secondary 包下。
  • Spring Boot 启动时会自动根据包路径绑定对应的数据源。

方案二:使用第三方组件(推荐:MyBatis-Plus Dynamic Datasource)

如果你觉得手动配置太繁琐,或者需要更灵活的切换(例如在同一个 Service 方法中切换),强烈推荐使用 dynamic-datasource-spring-boot-starter(由 MyBatis-Plus 团队维护)。

1. 引入依赖

xml
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
    <version>4.3.0</version> <!-- 请检查最新版本 -->
</dependency>

2. 配置文件 (application.yml)

结构非常清晰,支持分组。

yaml
spring:
  datasource:
    dynamic:
      # 设置默认数据源
      primary: master 
      # 严格模式,匹配不到数据源报错
      strict: false 
      datasource:
        master:
          url: jdbc:mysql://localhost:3306/db_master
          username: root
          password: password
          driver-class-name: com.mysql.cj.jdbc.Driver
        slave_1:
          url: jdbc:mysql://localhost:3306/db_slave
          username: root
          password: password
          driver-class-name: com.mysql.cj.jdbc.Driver

3. 使用注解切换

直接在 Service 层或 Mapper 层使用 @DS 注解即可。

java
@Service
public class UserService {

    @Autowired
    private UserMapper userMapper;

    // 不加注解,默认使用 primary 配置的数据源 (master)
    public void addUser(User user) {
        userMapper.insert(user);
    }

    // 切换到 slave_1 数据源
    @DS("slave_1")
    public List<User> getAllUsers() {
        return userMapper.selectList(null);
    }
}

方案对比与总结

特性 方案一:原生手动配置 (分包) 方案二:Dynamic Datasource (注解)
复杂度 高,需要写多个 Config 类 低,引入依赖改 YAML 即可
灵活性 较低,绑定在包路径上 高,可精确到方法级或类级
事务处理 只能处理单库事务,跨库需 JTA 支持多数据源切换,但跨库事务仍需注意
适用场景 业务模块完全独立,数据库物理隔离 读写分离、多租户系统、混合业务

建议:

  • 如果是新项目且追求开发效率,首选 方案二
  • 如果是老项目改造或者对第三方库有严格限制,使用 方案一

注意事项:事务问题

无论哪种方案,@Transactional 默认只能管理一个数据源的事务

  • 在方案一中,你需要指定事务管理器:@Transactional(transactionManager = "secondaryTransactionManager")
  • 如果一个方法同时操作两个数据库,普通的 @Transactional 无法保证原子性(即无法同时回滚两个库),这涉及到分布式事务(如使用 Seata 或 Atomikos)。
00:00
00:00