在 Spring Boot 项目中,动态切换数据源(数据库)通常需要使用 AbstractRoutingDataSource,结合 Spring AOP 或 ThreadLocal 机制来动态选择不同的数据源。以下是实现步骤:
1. 引入必要的依赖
如果使用 Spring Boot + MyBatis + 数据库,通常需要添加如下依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.2</version>
</dependency>
2. 定义动态数据源上下文
创建 DynamicDataSourceContextHolder,使用 ThreadLocal
记录当前线程的数据源。
public class DynamicDataSourceContextHolder {
private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();
// 设置数据源
public static void setDataSourceKey(String key) {
CONTEXT_HOLDER.set(key);
}
// 获取当前数据源
public static String getDataSourceKey() {
return CONTEXT_HOLDER.get();
}
// 清除数据源
public static void clearDataSourceKey() {
CONTEXT_HOLDER.remove();
}
}
3. 创建动态数据源
继承 AbstractRoutingDataSource
,实现 determineCurrentLookupKey()
方法。
@Component
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DynamicDataSourceContextHolder.getDataSourceKey();
}
}
4. 配置多个数据源
在 DataSourceConfig
配置类中,初始化多个数据源,并将 DynamicDataSource
设置为主数据源。
@Configuration
public class DataSourceConfig {
@Bean(name = "dataSource1")
@ConfigurationProperties(prefix = "spring.datasource.db1")
public DataSource dataSource1() {
return DataSourceBuilder.create().build();
}
@Bean(name = "dataSource2")
@ConfigurationProperties(prefix = "spring.datasource.db2")
public DataSource dataSource2() {
return DataSourceBuilder.create().build();
}
@Bean(name = "dynamicDataSource")
public DataSource dynamicDataSource(@Qualifier("dataSource1") DataSource dataSource1,
@Qualifier("dataSource2") DataSource dataSource2) {
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put("db1", dataSource1);
targetDataSources.put("db2", dataSource2);
DynamicDataSource dynamicDataSource = new DynamicDataSource();
dynamicDataSource.setTargetDataSources(targetDataSources);
dynamicDataSource.setDefaultTargetDataSource(dataSource1); // 默认数据源
return dynamicDataSource;
}
@Bean
public SqlSessionFactory sqlSessionFactory(@Qualifier("dynamicDataSource") DataSource dynamicDataSource) throws Exception {
SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
sessionFactoryBean.setDataSource(dynamicDataSource);
return sessionFactoryBean.getObject();
}
}
配置 application.yml
:
spring:
datasource:
db1:
url: jdbc:mysql://localhost:3306/db1
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
db2:
url: jdbc:mysql://localhost:3306/db2
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
5. 通过 AOP 动态切换数据源
使用自定义注解 + AOP 来实现方法级别的数据源切换。
5.1 创建自定义注解
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {
String value() default "db1"; // 默认数据源
}
5.2 创建 AOP 切面
@Aspect
@Component
public class DynamicDataSourceAspect {
@Pointcut("@annotation(com.example.datasource.DataSource)")
public void dataSourcePointCut() {}
@Around("dataSourcePointCut() && @annotation(dataSource)")
public Object around(ProceedingJoinPoint point, DataSource dataSource) throws Throwable {
// 切换数据源
DynamicDataSourceContextHolder.setDataSourceKey(dataSource.value());
try {
return point.proceed();
} finally {
DynamicDataSourceContextHolder.clearDataSourceKey();
}
}
}
6. 在业务层使用动态数据源
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
@DataSource("db1") // 使用 db1 数据库
public List<User> getUsersFromDB1() {
return userMapper.findAll();
}
@DataSource("db2") // 使用 db2 数据库
public List<User> getUsersFromDB2() {
return userMapper.findAll();
}
}
7. 编写 MyBatis Mapper
@Mapper
public interface UserMapper {
@Select("SELECT * FROM user")
List<User> findAll();
}
8. 启动项目并测试
启动 Spring Boot 应用后,可以调用:
getUsersFromDB1()
方法获取db1
数据库的数据getUsersFromDB2()
方法获取db2
数据库的数据
这样就实现了 Spring Boot 的 动态数据源切换。
总结
- 使用
ThreadLocal
记录当前数据源,避免多线程问题。 - 继承
AbstractRoutingDataSource
实现动态数据源。 - 在
DataSourceConfig
中配置多个数据源,并使用DynamicDataSource
进行管理。 - 通过 AOP 和自定义注解动态切换数据源,使得方法级别的数据源切换更方便。
- 在业务层使用注解
@DataSource("dbX")
进行数据源切换,实现灵活操作。