文章目录
- 前言
- 一、多数据源配置与切换方案
- 二、实现步骤
-
- [1. 创建多个 `DataSource` 配置类](#1. 创建多个
DataSource
配置类) - [2. 创建 `DataSource` 配置类](#2. 创建
DataSource
配置类) - [3. 创建动态数据源路由类](#3. 创建动态数据源路由类)
- [4. 实现 `DynamicDataSource` 类](#4. 实现
DynamicDataSource
类) - [5. 创建 `DataSourceContextHolder` 来存储当前的数据源标识](#5. 创建
DataSourceContextHolder
来存储当前的数据源标识) - [6. AOP 方式切换数据源](#6. AOP 方式切换数据源)
- [7. 自定义注解来指定数据源](#7. 自定义注解来指定数据源)
- [8. 在 Service 层使用注解指定数据源](#8. 在 Service 层使用注解指定数据源)
- [1. 创建多个 `DataSource` 配置类](#1. 创建多个
- 总结
前言
在 Spring Boot 中实现多数据源连接和切换,可以通过以下几种方案来实现,具体取决于项目的需求、数据库的使用模式和管理的复杂性。以下是一个常见的多数据源切换的实现方案,使用 AbstractRoutingDataSource 来动态选择数据源。
一、多数据源配置与切换方案
在多数据源场景中,通常有如下步骤:
- 配置多个数据源的
DataSource
bean。 - 使用
AbstractRoutingDataSource
来动态切换数据源。 - 使用
ThreadLocal
存储当前的数据库类型或数据源标识符。 - 配置数据源切换的逻辑,例如基于当前的用户、请求路径、服务标识等来选择不同的数据源。
二、实现步骤
1. 创建多个 DataSource
配置类
首先,为每个数据源创建单独的配置类,通常你会在 application.yml
或 application.properties
中配置每个数据源的连接信息。
spring:
datasource:
# 默认数据源配置
primary:
url: jdbc:mysql://localhost:3306/primary_db
username: root
password: password
driver-class-name: com.mysql.cj.jdbc.Driver
hikari:
maximum-pool-size: 10
# 第二数据源配置
secondary:
url: jdbc:mysql://localhost:3306/secondary_db
username: root
password: password
driver-class-name: com.mysql.cj.jdbc.Driver
hikari:
maximum-pool-size: 10
2. 创建 DataSource
配置类
@Configuration
@EnableTransactionManagement
public class DataSourceConfig {
@Bean(name = "primaryDataSource")
@Primary
@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. 创建动态数据源路由类
AbstractRoutingDataSource
允许我们在运行时根据某些条件动态选择数据源。
@Configuration
public class DynamicDataSourceConfig {
@Autowired
@Qualifier("primaryDataSource")
private DataSource primaryDataSource;
@Autowired
@Qualifier("secondaryDataSource")
private DataSource secondaryDataSource;
@Bean
public DataSource dataSource() {
// 创建一个路由数据源
DynamicDataSource dataSource = new DynamicDataSource();
dataSource.setDefaultTargetDataSource(primaryDataSource); // 默认数据源
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put("primary", primaryDataSource);
targetDataSources.put("secondary", secondaryDataSource);
dataSource.setTargetDataSources(targetDataSources);
return dataSource;
}
}
4. 实现 DynamicDataSource
类
DynamicDataSource
是继承自 AbstractRoutingDataSource
,它通过 determineCurrentLookupKey()
方法来动态确定当前的数据源。
public class DynamicDataSource extends AbstractRoutingDataSource {
// 从 ThreadLocal 获取当前的数据库标识
@Override
protected Object determineCurrentLookupKey() {
return DataSourceContextHolder.getDataSourceType();
}
}
5. 创建 DataSourceContextHolder
来存储当前的数据源标识
使用 ThreadLocal
来保持当前线程的数据库标识,以便在不同的数据源之间切换。
public class DataSourceContextHolder {
// 使用 ThreadLocal 存储当前线程的数据源标识
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();
}
}
6. AOP 方式切换数据源
为了在运行时动态切换数据源,通常会使用 AOP 切面来拦截方法执行并指定数据源。
@Aspect
@Component
public class DataSourceAspect {
// 通过注解来指定使用哪个数据源
@Before("@annotation(dataSource)")
public void switchDataSource(DataSourceType dataSource) {
// 切换数据源
DataSourceContextHolder.setDataSourceType(dataSource.value());
}
@After("@annotation(dataSource)")
public void clearDataSource(DataSourceType dataSource) {
// 清理数据源标识,避免影响其他线程
DataSourceContextHolder.clearDataSourceType();
}
}
7. 自定义注解来指定数据源
创建一个自定义注解 DataSourceType
,用于指定当前方法执行时需要使用的数据源。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DataSourceType {
String value() default "primary"; // 数据源标识,默认使用primary数据源
}
8. 在 Service 层使用注解指定数据源
在 Service 层,可以使用刚才创建的 @DataSourceType
注解来指定不同的方法使用不同的数据源。
@Service
public class UserService {
@DataSourceType("primary")
public List<User> getPrimaryUsers() {
// 查询主数据库
return userRepository.findAll();
}
@DataSourceType("secondary")
public List<User> getSecondaryUsers() {
// 查询次数据库
return secondaryUserRepository.findAll();
}
}
总结
- 数据源配置 :为每个数据源配置
DataSource
Bean。 - 动态数据源路由 :使用
AbstractRoutingDataSource
来实现动态切换数据源。 - ThreadLocal存储 :使用
ThreadLocal
存储和获取当前线程的数据源标识。 - AOP切换数据源:使用 AOP 来拦截方法并切换数据源。
- 注解方式指定数据源:通过自定义注解来指定方法使用的具体数据源。
这种方式比较灵活,能够在运行时根据业务需求选择不同的数据源,适用于多数据源的场景,尤其是分库分表、读写分离等复杂应用场景。