说明
在Spring Boot中配置多个数据源并实现自动切换,可以通过使用AbstractRoutingDataSource和AOP(面向切面编程)的方式来实现。
下面是一个基本的步骤指南(仅供参考):
定义数据源配置
在application.properties或application.yml中定义每个数据源的连接信息。
# 主数据源
spring.datasource.primary.url=jdbc:mysql://localhost:3306/primary_db
spring.datasource.primary.username=root
spring.datasource.primary.password=password
spring.datasource.primary.driver-class-name=com.mysql.cj.jdbc.Driver
# 次数据源
spring.datasource.secondary.url=jdbc:mysql://localhost:3306/secondary_db
spring.datasource.secondary.username=root
spring.datasource.secondary.password=password
spring.datasource.secondary.driver-class-name=com.mysql.cj.jdbc.Driver
创建数据源配置类
为每个数据源创建配置相应的DataSource。
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
/**
* 数据源配置
* @author zq
* @since 14:32 2024/03/20
**/
@Configuration
public class MoreDataSourceConfig {
/**
* 配置主数据源 ,用于写入数据
* @author zq
* @since 14:27 2024/03/20
* @return javax.sql.DataSource
**/
@Bean(name = "primaryDataSource")
@ConfigurationProperties(prefix = "spring.datasource.primary")
public DataSource primaryDataSource() {
return new DruidDataSource();
}
/**
* 配置从数据源 ,用于读取数据
* @author zq
* @since 14:27 2024/03/20
* @return javax.sql.DataSource
**/
@Bean(name = "secondaryDataSource")
@ConfigurationProperties(prefix = "spring.datasource.secondary")
public DataSource secondaryDataSource() {
//return DataSourceBuilder.create().build();
return new DruidDataSource();
}
// ...
}
创建动态数据源
实现一个继承自AbstractRoutingDataSource的动态数据源类,用来根据上下文切换数据源。
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DataSourceContextHolder.getDataSourceType();
}
}
DataSourceContextHolder是一个用来保存当前线程的数据源键的工具类。
public class DataSourceContextHolder {
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();
}
}
注册动态数据源
在配置类中注册动态数据源,并将所有数据源设置到动态数据源中。
@Configuration
public class DataSourceConfig {
@Autowired
@Qualifier("primaryDataSource")
private DataSource dataSourcePrimary;
@Autowired
@Qualifier("secondaryDataSource")
private DataSource dataSourceSecondary;
@Bean(name = "dataSource")
public DataSource dataSource() {
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put("primary", dataSourcePrimary);
targetDataSources.put("secondary", dataSourceSecondary);
DynamicDataSource dataSource = new DynamicDataSource();
dataSource.setTargetDataSources(targetDataSources);
dataSource.setDefaultTargetDataSource(dataSourcePrimary); // 设置默认数据源
return dataSource;
}
// ... 其他配置 ...
}
使用AOP实现数据源自动切换
通过AOP在方法执行前设置数据源,并在方法执行后清除数据源。
@Aspect
@Component
public class DataSourceAspect {
@Before("@annotation(dataSource)")
public void switchDataSource(JoinPoint point, DataSourceSwitch dataSource) {
DataSourceContextHolder.setDataSourceType(dataSource.value());
}
@After("@annotation(dataSource)")
public void restoreDataSource(JoinPoint point, DataSourceSwitch dataSource) {
DataSourceContextHolder.clearDataSourceType();
}
}
DataSourceSwitch是一个自定义注解,用来标识需要切换数据源的方法。
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface DataSourceSwitch {
String value() default "primary";
}
在需要切换数据源的方法上使用注解
在需要切换数据源的服务层或DAO层方法上添加@DataSourceSwitch注解,并指定数据源类型。
@Service
public class SomeService {
// 调用secondary数据源的进行读取操作
@DataSourceSwitch("secondary")
public void someMethodUsingSecondaryDataSource() {
}
// 默认使用primary数据源的写入操作
@DataSourceSwitch
public void someMethodUsingDefaultDataSource() {
}
}
事务管理说明
如果你的业务操作需要在事务中执行,确保你的事务管理器能够处理数据源的切换。这可能需要你自定义事务管理器,或者使用AOP来在事务开始之前设置正确的数据源。
注意事项
通过以上步骤,实现了在Spring Boot中配置多个数据源,并可以通过注解的方式自动切换数据源。在调用带有@DataSourceSwitch
注解的方法时,AOP切面会自动将数据源切换到指定的数据源,并在方法执行完毕后恢复默认数据源。
当使用JPA时,确保每个数据源都有自己的EntityManagerFactory和TransactionManager。
考虑到线程安全问题,确保你的数据源切换逻辑是线程安全的。
数据源切换通常用于读取和写入操作分离的场景,或者当你有多个独立的数据库需要访问时。确保不要过度使用,因为它可能增加复杂性和出错的可能性。
请注意,这是一个高级功能,并且可能会引入额外的复杂性和潜在的问题。在生产环境中使用之前,请确保彻底测试你的实现。