SpringBoot面试题 - 在Spring Boot 中如何实现多数据源配置?
引言
在现代企业应用开发中,一个应用需要连接多个数据库的场景越来越常见。Spring Boot提供了灵活的方式来实现多数据源配置,本文将详细介绍如何在Spring Boot项目中配置和使用多个数据源。
一、多数据源的应用场景
多数据源配置通常用于以下场景:
- 读写分离(主从数据库)
- 微服务架构中访问多个服务数据库
- 分库分表场景
- 访问异构数据库(如同时使用MySQL和Oracle)
二、基础配置实现
1. 添加依赖
首先,在pom.xml
中添加必要的依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
2. 配置多个数据源
在application.properties
或application.yml
中配置多个数据源:
# 主数据源配置
spring.datasource.primary.url=jdbc:mysql://localhost:3306/db1
spring.datasource.primary.username=root
spring.datasource.primary.password=123456
spring.datasource.primary.driver-class-name=com.mysql.cj.jdbc.Driver
# 次数据源配置
spring.datasource.secondary.url=jdbc:mysql://localhost:3306/db2
spring.datasource.secondary.username=root
spring.datasource.secondary.password=123456
spring.datasource.secondary.driver-class-name=com.mysql.cj.jdbc.Driver
3. 创建数据源配置类
@Configuration
public class DataSourceConfig {
@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();
}
}
三、多数据源工作流程
四、JPA与多数据源集成
1. 配置主数据源的JPA
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
entityManagerFactoryRef = "primaryEntityManagerFactory",
transactionManagerRef = "primaryTransactionManager",
basePackages = {"com.example.repository.primary"}
)
public class PrimaryJpaConfig {
@Autowired
@Qualifier("primaryDataSource")
private DataSource primaryDataSource;
@Bean(name = "primaryEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean primaryEntityManagerFactory(
EntityManagerFactoryBuilder builder) {
return builder
.dataSource(primaryDataSource)
.packages("com.example.entity.primary")
.persistenceUnit("primary")
.build();
}
@Bean(name = "primaryTransactionManager")
public PlatformTransactionManager primaryTransactionManager(
@Qualifier("primaryEntityManagerFactory") EntityManagerFactory primaryEntityManagerFactory) {
return new JpaTransactionManager(primaryEntityManagerFactory);
}
}
2. 配置次数据源的JPA
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
entityManagerFactoryRef = "secondaryEntityManagerFactory",
transactionManagerRef = "secondaryTransactionManager",
basePackages = {"com.example.repository.secondary"}
)
public class SecondaryJpaConfig {
@Autowired
@Qualifier("secondaryDataSource")
private DataSource secondaryDataSource;
@Bean(name = "secondaryEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean secondaryEntityManagerFactory(
EntityManagerFactoryBuilder builder) {
return builder
.dataSource(secondaryDataSource)
.packages("com.example.entity.secondary")
.persistenceUnit("secondary")
.build();
}
@Bean(name = "secondaryTransactionManager")
public PlatformTransactionManager secondaryTransactionManager(
@Qualifier("secondaryEntityManagerFactory") EntityManagerFactory secondaryEntityManagerFactory) {
return new JpaTransactionManager(secondaryEntityManagerFactory);
}
}
五、MyBatis与多数据源集成
1. 配置主数据源的MyBatis
@Configuration
@MapperScan(basePackages = "com.example.mapper.primary", sqlSessionFactoryRef = "primarySqlSessionFactory")
public class PrimaryMyBatisConfig {
@Bean(name = "primarySqlSessionFactory")
public SqlSessionFactory primarySqlSessionFactory(@Qualifier("primaryDataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
bean.setMapperLocations(new PathMatchingResourcePatternResolver()
.getResources("classpath:mapper/primary/*.xml"));
return bean.getObject();
}
@Bean(name = "primaryTransactionManager")
public DataSourceTransactionManager primaryTransactionManager(@Qualifier("primaryDataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
@Bean(name = "primarySqlSessionTemplate")
public SqlSessionTemplate primarySqlSessionTemplate(
@Qualifier("primarySqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
六、动态数据源切换
对于更复杂的场景,可以实现动态数据源路由:
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DataSourceContextHolder.getDataSourceType();
}
}
public class DataSourceContextHolder {
private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();
public static void setDataSourceType(String dataSourceType) {
contextHolder.set(dataSourceType);
}
public static String getDataSourceType() {
return contextHolder.get();
}
public static void clearDataSourceType() {
contextHolder.remove();
}
}
动态数据源切换流程:
七、多数据源事务管理
在多数据源环境下,事务管理变得更加复杂。可以使用JTA或分布式事务解决方案:
@Bean
public JtaTransactionManager transactionManager() {
JtaTransactionManager transactionManager = new JtaTransactionManager();
transactionManager.setUserTransaction(userTransaction());
transactionManager.setTransactionManager(atomikosTransactionManager());
return transactionManager;
}
@Bean(initMethod = "init", destroyMethod = "close")
public UserTransactionManager atomikosTransactionManager() {
UserTransactionManager userTransactionManager = new UserTransactionManager();
userTransactionManager.setForceShutdown(false);
return userTransactionManager;
}
@Bean
public UserTransaction userTransaction() throws SystemException {
UserTransactionImp userTransactionImp = new UserTransactionImp();
userTransactionImp.setTransactionTimeout(300);
return userTransactionImp;
}
八、最佳实践与注意事项
- 明确的包结构:为不同数据源的实体、仓库和服务划分明确的包路径
- 事务边界:清楚界定每个事务使用哪个数据源
- 性能考虑:避免在单个请求中频繁切换数据源
- 连接池配置:为每个数据源单独配置连接池参数
- 监控:实现多数据源的监控和健康检查
九、总结
Spring Boot提供了灵活的多数据源配置方式,无论是使用JPA还是MyBatis,都可以通过适当的配置实现多数据源访问。关键点在于:
- 正确配置多个DataSource bean
- 为每个数据源配置独立的事务管理器
- 明确划分不同数据源对应的仓库和实体类包路径
- 在复杂场景下考虑动态数据源路由或分布式事务
通过本文的介绍,您应该能够在Spring Boot项目中成功配置和使用多个数据源,满足各种复杂的业务需求。