You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Spring Boot同数据库服务器多数据源/多Schema配置技术问询

Spring Boot 多数据源/多Schema 配置实操方案

嘿,我刚好在几个项目里配置过Spring Boot的多数据源,针对你提到的实体foo归属DataSource A、实体bar归属DataSource B的场景,给你一套实操性很强的实现方案,步骤清晰,还会提一些容易踩的坑:

1. 先在配置文件里定义两个数据源的连接信息

不管用application.yml还是application.properties,都要分别把两个数据源的URL、账号密码、驱动类配置清楚,比如用yml的写法:

spring:
  datasource:
    # 数据源A - 对应foo实体所在的库/Schema
    ds-a:
      url: jdbc:mysql://localhost:3306/db_a?useSSL=false&serverTimezone=UTC
      username: root
      password: your_db_password
      driver-class-name: com.mysql.cj.jdbc.Driver
    # 数据源B - 对应bar实体所在的库/Schema
    ds-b:
      url: jdbc:mysql://localhost:3306/db_b?useSSL=false&serverTimezone=UTC
      username: root
      password: your_db_password
      driver-class-name: com.mysql.cj.jdbc.Driver

如果是同一服务器下的不同Schema,只需要修改URL里的Schema参数就行,比如MySQL可以加&currentSchema=schema_a来指定Schema。

2. 注册两个数据源的Bean

创建一个配置类,把两个数据源分别注册成Spring Bean,注意给它们起不同的名称,方便后续关联:

@Configuration
public class DataSourceConfig {

    // 数据源A的Bean
    @Bean(name = "dataSourceA")
    @ConfigurationProperties(prefix = "spring.datasource.ds-a")
    public DataSource dataSourceA() {
        return DataSourceBuilder.create().build();
    }

    // 数据源B的Bean
    @Bean(name = "dataSourceB")
    @ConfigurationProperties(prefix = "spring.datasource.ds-b")
    public DataSource dataSourceB() {
        return DataSourceBuilder.create().build();
    }
}

3. 为每个数据源配置独立的JPA环境

这一步是核心,要给两个数据源分别配置EntityManagerFactory和事务管理器,还要指定各自的实体包、Repository包,避免混淆:

数据源A的JPA配置(绑定foo实体)

@Configuration
@EnableJpaRepositories(
        basePackages = "com.yourproject.repository.a", // foo对应的Repository所在包
        entityManagerFactoryRef = "entityManagerFactoryA",
        transactionManagerRef = "transactionManagerA"
)
public class JpaConfigA {

    @Autowired
    @Qualifier("dataSourceA")
    private DataSource dataSourceA;

    @Bean(name = "entityManagerFactoryA")
    public LocalContainerEntityManagerFactoryBean entityManagerFactoryA(EntityManagerFactoryBuilder builder) {
        return builder
                .dataSource(dataSourceA)
                .packages("com.yourproject.entity.a") // foo实体所在的包
                .persistenceUnit("ds-a")
                .build();
    }

    @Bean(name = "transactionManagerA")
    public PlatformTransactionManager transactionManagerA(
            @Qualifier("entityManagerFactoryA") EntityManagerFactory entityManagerFactoryA) {
        return new JpaTransactionManager(entityManagerFactoryA);
    }
}

数据源B的JPA配置(绑定bar实体)

@Configuration
@EnableJpaRepositories(
        basePackages = "com.yourproject.repository.b", // bar对应的Repository所在包
        entityManagerFactoryRef = "entityManagerFactoryB",
        transactionManagerRef = "transactionManagerB"
)
public class JpaConfigB {

    @Autowired
    @Qualifier("dataSourceB")
    private DataSource dataSourceB;

    @Bean(name = "entityManagerFactoryB")
    public LocalContainerEntityManagerFactoryBean entityManagerFactoryB(EntityManagerFactoryBuilder builder) {
        return builder
                .dataSource(dataSourceB)
                .packages("com.yourproject.entity.b") // bar实体所在的包
                .persistenceUnit("ds-b")
                .build();
    }

    @Bean(name = "transactionManagerB")
    public PlatformTransactionManager transactionManagerB(
            @Qualifier("entityManagerFactoryB") EntityManagerFactory entityManagerFactoryB) {
        return new JpaTransactionManager(entityManagerFactoryB);
    }
}

4. 规范实体和Repository的包结构

按照上面配置的包路径,把实体和Repository分开存放:

  • Foo实体放在com.yourproject.entity.a,对应的FooRepository放在com.yourproject.repository.a
  • Bar实体放在com.yourproject.entity.b,对应的BarRepository放在com.yourproject.repository.b

之后在业务代码里注入对应的Repository,Spring就会自动关联到正确的数据源了,比如:

@Service
public class FooService {
    private final FooRepository fooRepository;

    // 构造注入(推荐)
    public FooService(FooRepository fooRepository) {
        this.fooRepository = fooRepository;
    }

    // 这个方法的操作会自动走DataSource A
    public Foo getFooById(Long id) {
        return fooRepository.findById(id).orElse(null);
    }
}

几个要注意的点

  • 如果需要跨数据源的事务,普通的JPA事务管理器搞不定,得用JTA事务管理器(比如Atomikos)
  • 实体类上可以用@Table(name = "foo", schema = "schema_a")明确指定Schema,和数据源配置配合更稳妥
  • 两个数据源的配置类要确保扫描范围不重叠,不然会出现Repository关联错误的问题

内容的提问来源于stack exchange,提问作者Ananth

火山引擎 最新活动