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可以加¤tSchema=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.aBar实体放在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




