前言
在复杂的业务场景中,一个应用可能需要连接多个数据库,并根据不同的业务需求动态地切换数据源。Spring Boot提供了强大的数据访问和配置管理能力,使得实现动态数据源切换变得相对简单。本文将介绍如何在Spring Boot项目中实现动态数据源切换,并提供示例代码。
1. 引入必要的依赖
首先,在你的Spring Boot项目的pom.xml
文件中引入必要的依赖。这些依赖包括Spring Boot Starter JDBC、数据库驱动、以及一个动态数据源解决方案(如dynamic-datasource-spring-boot-starter
,或者你可以选择自定义实现)。
<dependencies>
<!-- Spring Boot Starter JDBC -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!-- MySQL 数据库驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- 动态数据源解决方案 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>最新版本</version>
</dependency>
</dependencies>
2. 配置数据源
在application.yml
或application.properties
文件中配置多个数据源。以下是一个application.yml
的配置示例:
2. 配置数据源
在application.yml或application.properties文件中配置多个数据源。以下是一个application.yml的配置示例:
3. 使用动态数据源
如果你使用dynamic-datasource-spring-boot-starter
,你不需要编写太多的切换逻辑,因为它已经提供了这些功能。你只需要在你的Service层或者需要切换数据源的任何地方,使用@DS
注解来指定数据源。
import org.springframework.stereotype.Service;
import com.baomidou.dynamic.datasource.annotation.DS;
@Service
public class SomeService {
@DS("slave")
public List<SomeData> findDataFromSlave() {
// 这里会切换到slave数据源
return someRepository.findAll();
}
@DS("master")
public void updateDataInMaster(SomeData data) {
// 这里会切换到master数据源
someRepository.save(data);
}
}
4. 自定义实现动态数据源切换(可选)
如果你不使用dynamic-datasource-spring-boot-starter
,你可以自定义实现动态数据源切换。这通常涉及到以下几个步骤:
创建一个继承自
AbstractRoutingDataSource
的类,并在其中实现determineCurrentLookupKey()
方法,该方法用于决定使用哪个数据源。创建一个数据源上下文类,用于存储当前线程使用的数据源。
在配置类中配置多个数据源,并将它们注册到动态数据源中。
以下是一个自定义实现的示例代码:
// 数据源上下文类
public class DataSourceContextHolder {
private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();
public static void setDataSource(String dataSource) {
contextHolder.set(dataSource);
}
public static String getDataSource() {
return contextHolder.get();
}
public static void clearDataSource() {
contextHolder.remove();
}
}
// 动态数据源类
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DataSourceContextHolder.getDataSource();
}
}
// 配置类
@Configuration
public class DataSourceConfig {
@Bean
@Primary
@ConfigurationProperties("spring.datasource.master")
public DataSource masterDataSource() {
return DataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties("spring.datasource.slave")
public DataSource slaveDataSource() {
return DataSourceBuilder.create().build();
}
@Bean
public DataSource dataSource() {
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put("master", masterDataSource());
targetDataSources.put("slave", slaveDataSource());
DynamicDataSource dataSource = new DynamicDataSource();
dataSource.setDefaultTargetDataSource(masterDataSource());
dataSource.setTargetDataSources(targetDataSources);
return dataSource;
}
}
// 使用AOP切换数据源(示例)
@Aspect
@Component
public class DataSourceAspect {
@Before("@annotation(dataSource)")
public void changeDataSource(JoinPoint point, DataSource dataSource) throws Throwable {
DataSourceContextHolder.setDataSource(dataSource.value());
}
@After("@annotation(dataSource)")
public void clearDataSource(JoinPoint point, DataSource dataSource) throws Throwable {
DataSourceContextHolder.clearDataSource();
}
}
// 自定义注解
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface DataSource {
String value() default "master";
}
在Service层使用自定义注解来指定数据源:
@Service
public class SomeService {
@DataSource("slave")
public List<SomeData> findDataFromSlave() {
// 这里会切换到slave数据源
return someRepository.findAll();
}
@DataSource("master")
public void updateDataInMaster(SomeData data) {
// 这里会切换到master数据源
someRepository.save(data);
}
}