文章目录
- 前言
- 一、多数据源配置与切换方案
- 二、实现步骤
- 1. 创建多个 `DataSource` 配置类
- 2. 创建 `DataSource` 配置类
- 3. 创建动态数据源路由类
- 4. 实现 `DynamicDataSource` 类
- 5. 创建 `DataSourceContextHolder` 来存储当前的数据源标识
- 6. AOP 方式切换数据源
- 7. 自定义注解来指定数据源
- 8. 在 Service 层使用注解指定数据源
- 总结
前言
在 Spring Boot 中实现多数据源连接和切换,可以通过以下几种方案来实现,具体取决于项目的需求、数据库的使用模式和管理的复杂性。以下是一个常见的多数据源切换的实现方案,使用 AbstractRoutingDataSource 来动态选择数据源。
一、多数据源配置与切换方案
在多数据源场景中,通常有如下步骤:
- 配置多个数据源的
DataSource
bean。 - 使用
AbstractRoutingDataSource
来动态切换数据源。 - 使用
ThreadLocal
存储当前的数据库类型或数据源标识符。 - 配置数据源切换的逻辑,例如基于当前的用户、请求路径、服务标识等来选择不同的数据源。
二、实现步骤
1. 创建多个 DataSource
配置类
首先,为每个数据源创建单独的配置类,通常你会在 application.yml
或 application.properties
中配置每个数据源的连接信息。
spring:datasource:# 默认数据源配置primary:url: jdbc:mysql://localhost:3306/primary_dbusername: rootpassword: passworddriver-class-name: com.mysql.cj.jdbc.Driverhikari:maximum-pool-size: 10# 第二数据源配置secondary:url: jdbc:mysql://localhost:3306/secondary_dbusername: rootpassword: passworddriver-class-name: com.mysql.cj.jdbc.Driverhikari:maximum-pool-size: 10
2. 创建 DataSource
配置类
java">@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
允许我们在运行时根据某些条件动态选择数据源。
java">@Configuration
public class DynamicDataSourceConfig {@Autowired@Qualifier("primaryDataSource")private DataSource primaryDataSource;@Autowired@Qualifier("secondaryDataSource")private DataSource secondaryDataSource;@Beanpublic 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()
方法来动态确定当前的数据源。
java">public class DynamicDataSource extends AbstractRoutingDataSource {// 从 ThreadLocal 获取当前的数据库标识@Overrideprotected Object determineCurrentLookupKey() {return DataSourceContextHolder.getDataSourceType();}
}
5. 创建 DataSourceContextHolder
来存储当前的数据源标识
使用 ThreadLocal
来保持当前线程的数据库标识,以便在不同的数据源之间切换。
java">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 切面来拦截方法执行并指定数据源。
java">@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
,用于指定当前方法执行时需要使用的数据源。
java">@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DataSourceType {String value() default "primary"; // 数据源标识,默认使用primary数据源
}
8. 在 Service 层使用注解指定数据源
在 Service 层,可以使用刚才创建的 @DataSourceType
注解来指定不同的方法使用不同的数据源。
java">@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 来拦截方法并切换数据源。
- 注解方式指定数据源:通过自定义注解来指定方法使用的具体数据源。
这种方式比较灵活,能够在运行时根据业务需求选择不同的数据源,适用于多数据源的场景,尤其是分库分表、读写分离等复杂应用场景。