在Spring Boot中实现动态数据源主要涉及到创建和管理不同的数据源,并在运行时根据需要切换。这可以通过编程方式配置Spring的AbstractRoutingDataSource来完成。下面我会逐步介绍如何实现动态数据源,并给出代码示例。
第1步:添加依赖
首先,你需要在pom.xml
文件中添加Spring Boot和数据库相关的依赖。以MySQL为例,你需要添加以下依赖:
<dependencies>
<!-- Spring Boot Starter Data JPA -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- MySQL Connector -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- Spring Boot Starter Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
第2步:配置数据源
创建一个配置类来配置多个数据源。首先,定义每个数据源的配置,并使用Spring的@ConfigurationProperties
来绑定配置属性。
@Configuration
public class DataSourceConfig {
@Bean
@ConfigurationProperties("app.datasource.master")
public DataSource masterDataSource() {
return DataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties("app.datasource.slave")
public DataSource slaveDataSource() {
return DataSourceBuilder.create().build();
}
@Primary
@Bean
public DataSource dynamicDataSource() {
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put("master", masterDataSource());
targetDataSources.put("slave", slaveDataSource());
RoutingDataSource routingDataSource = new RoutingDataSource();
routingDataSource.setDefaultTargetDataSource(masterDataSource());
routingDataSource.setTargetDataSources(targetDataSources);
return routingDataSource;
}
}
第3步:实现AbstractRoutingDataSource
接下来,你需要实现AbstractRoutingDataSource
,以便根据当前上下文确定应使用哪个数据源。
public class RoutingDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DataSourceContext.getCurrentDataSource();
}
}
第4步:上下文持有器
创建一个DataSourceContext
类来持有当前请求的数据源标识。
public class DataSourceContext {
private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();
public static void setCurrentDataSource(String dataSourceType) {
contextHolder.set(dataSourceType);
}
public static String getCurrentDataSource() {
return contextHolder.get();
}
public static void clear() {
contextHolder.remove();
}
}
第5步:使用切面来切换数据源
你可以使用Spring AOP在方法执行前动态切换数据源。下面是一个示例:
@Aspect
@Component
public class DataSourceAspect {
@Before("@annotation(targetDataSource)")
public void switchDataSource(TargetDataSource targetDataSource) {
DataSourceContext.setCurrentDataSource(targetDataSource.value());
}
@After("@annotation(targetDataSource)")
public void restoreDataSource(TargetDataSource targetDataSource) {
DataSourceContext.clear();
}
}
其中TargetDataSource
是一个自定义注解,用来在方法上标记需要切换的数据源。
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface TargetDataSource {
String value();
}
第6步:配置application.properties
在application.properties
中配置你的数据源:
app.datasource.master.jdbc-url=jdbc:mysql://localhost:3306/masterdb
app.datasource.master.username=root
app.datasource.master.password=pass
app.datasource.slave.jdbc-url=jdbc:mysql://localhost:3306/slavedb
app.datasource.slave.username=root
app.datasource.slave.password=pass
通过以上步骤,你可以在Spring Boot应用中实现动态数据源,根据不同的需求切换到不同的数据库。这种方式特别适合于读写分离和多租户场景。
7、补充
实际使用中,偏业务的代码往往是根据业务相关ID做计算后放入DataSourceContext数据的,也就是同一个Method,不同的租户/用户使用的数据源可能是不一样的,这才更符合多租户系统的需求。而偏功能的系统,它的Method才有可能是固定的数据源(注解指定)。因此多租户的系统请求过来,我们可以通过在过滤器或者全局拦截器里计算值,然后写数据。