在Spring Boot应用中,可以通过配置多个数据源,并且动态地在这些数据源之间进行切换。实现动态切换数据源的核心原理是使用了ThreadLocal存储当前数据源的标识,在需要使用数据源的时候根据标识从多个数据源中选择一个进行使用。
具体来说,可以通过以下步骤实现Spring Boot数据源的动态切换:
1.配置多个数据源
在Spring Boot的配置文件中,可以配置多个数据源。例如,可以配置两个MySQL数据源和一个Redis数据源。
2.定义数据源切换工具类
定义一个数据源切换工具类,该工具类包含一个ThreadLocal变量和相关方法。在该工具类的方法中,可以根据当前线程中保存的数据源标识,获取对应的数据源。
3.实现数据源切换AOP
在Spring Boot应用中,可以使用AOP技术实现数据源的动态切换。在AOP切面中,可以通过切点匹配需要切换数据源的方法,然后在方法执行前根据数据源标识切换数据源。例如,可以在DAO层的方法上进行切面编程,实现在DAO层动态切换数据源。
下面是个例子:
在 application.properties 文件中配置多个数据源
# 数据源1配置
spring.datasource.dataSource1.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.dataSource1.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.dataSource1.url=jdbc:mysql://localhost:3306/db1
spring.datasource.dataSource1.username=root
spring.datasource.dataSource1.password=123456
# 数据源2配置
spring.datasource.dataSource2.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.dataSource2.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.dataSource2.url=jdbc:mysql://localhost:3306/db2
spring.datasource.dataSource2.username=root
spring.datasource.dataSource2.password=123456
配置多个 SqlSessionFactory,每个 SqlSessionFactory 对应一个数据源:
@Configuration
public class MybatisConfig {
@Bean(name = "sqlSessionFactory1")
public SqlSessionFactory sqlSessionFactory1(@Qualifier("dataSource1") DataSource dataSource) throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
// 其他配置...
return bean.getObject();
}
@Bean(name = "sqlSessionFactory2")
public SqlSessionFactory sqlSessionFactory2(@Qualifier("dataSource2") DataSource dataSource) throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
// 其他配置...
return bean.getObject();
}
}
实现动态数据源切换,可以通过继承 AbstractRoutingDataSource 实现:
public class DynamicDataSource extends AbstractRoutingDataSource {
private static final ThreadLocal<String> dataSourceKey = new ThreadLocal<>();
public static void setDataSourceKey(String dataSource) {
dataSourceKey.set(dataSource);
}
@Override
protected Object determineCurrentLookupKey() {
return dataSourceKey.get();
}
}
配置动态数据源和动态切换拦截器:
@Configuration
public class DataSourceConfig {
@Bean
public DataSource dataSource(@Qualifier("dataSource1") DataSource dataSource1,
@Qualifier("dataSource2") DataSource dataSource2) {
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put("dataSource1", dataSource1);
targetDataSources.put("dataSource2", dataSource2);
DynamicDataSource dynamicDataSource = new DynamicDataSource();
dynamicDataSource.setDefaultTargetDataSource(dataSource1);
dynamicDataSource.setTargetDataSources(targetDataSources);
return dynamicDataSource;
}
@Bean
public SqlSessionTemplate sqlSessionTemplate(@Qualifier("sqlSessionFactory1") SqlSessionFactory sqlSessionFactory1,
@Qualifier("sqlSessionFactory2") SqlSessionFactory sqlSessionFactory2) {
Map<Object, SqlSessionFactory> sqlSessionFactoryMap = new HashMap<>();
sqlSessionFactoryMap.put("dataSource1", sqlSessionFactory1);
sqlSessionFactoryMap.put("dataSource2", sqlSessionFactory2);
SqlSessionTemplate sqlSessionTemplate = new SqlSessionTemplate(sqlSessionFactory1);
sqlSessionTemplate.setTargetSqlSessionFactorys(Arrays.asList(sqlSessionFactory1, sqlSessionFactory2));
sqlSessionTemplate.setSqlSessionFactorys(sqlSessionFactoryMap.values());
return sqlSessionTemplate;
}
@Bean
public DynamicDataSourceInterceptor dynamicDataSourceInterceptor() {
DynamicDataSourceInterceptor interceptor = new DynamicDataSourceInterceptor();
interceptor.setSqlSessionFactoryKey("dataSourceKey");
return interceptor;
}
}
配置 DynamicDataSourceInterceptor 拦截器:
public class DynamicDataSourceInterceptor extends AbstractInterceptor {
private String sqlSessionFactoryKey;
public void setSqlSessionFactoryKey(String sqlSessionFactoryKey) {
this.sqlSessionFactoryKey = sqlSessionFactoryKey;
}
@Override
public Object intercept(Invocation invocation) throws Throwable {
if (sqlSessionFactoryKey != null) {
String dataSource = DynamicDataSourceContextHolder.getDataSourceKey();
if (dataSource != null) {
MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
String mappedStatementId = mappedStatement.getId();
Configuration configuration = mappedStatement.getConfiguration();
String dataSourceId = mappedStatementId.substring(0, mappedStatementId.lastIndexOf("."));
DataSourceContextHolder.setDataSourceKey(dataSource);
if (configuration.getEnvironment().getId().equals(dataSourceId)) {
configuration.setEnvironment(new Environment(dataSourceId, new JdbcTransactionFactory(), DynamicDataSourceContextHolder.getDataSource(dataSource)));
}
}
}
return invocation.proceed();
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
}
6. 在需要切换数据源的地方调用 `DynamicDataSourceContextHolder.setDataSourceKey()` 方法切换数据源即可。