简单介绍
实现数据源切换的功能就是自定义一个类扩展AbstractRoutingDataSource抽象类,其实相当于数据源的路由中介,可以实现在项目运行时。根据相应的key值切换到对应的DataSource上。
DynamicDataSource类
package com.picc.agricultural.service;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
public class DynamicDataSource extends AbstractRoutingDataSource {
/**调用流程
* public Connection getConnection() throws SQLException {
return this.determineTargetDataSource().getConnection();
}
调用从AbstractRoutingDataSource继承的determineTargetDataSource方法
protected DataSource determineTargetDataSource() {
调用本类中的determineCurrentLookupKey,从threadlocal中将datasouce的key值取出
Object lookupKey = this.determineCurrentLookupKey();
根据key拿到真正的dataSource
DataSource dataSource = (DataSource)this.resolvedDataSources.get(lookupKey);
}
如果没有拿到,就是用默认的datasource
*/
//从当前线程中取出放进去的dataource的键值
protected Object determineCurrentLookupKey() {
return DataSourceHolder.getDataSource();
}
}
DataSourceHolder 类
public class DataSourceHolder {
private final static ThreadLocal<String> datasources = new ThreadLocal<String>();
/**
* 设置已知名字的数据源
* @param source
*/
public static void setDatasources(String source) {
datasources.set(source);
}
/**
* 获取当前正在使用的数据源的名字
* @return
*/
public static String getDataSource() {
return datasources.get();
}
/**
* 清空数据源
*/
public static void clearDataSources() {
datasources.remove();
}
}
TargetDataSource类
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TargetDataSource {
public String value() default "";
}
DataSourceConfig 类
@Component
@ConfigurationProperties(prefix = "spring.datasource")
@Data
public class DataSourceConfig {
private List<DataSourceModel> jdbc;
public Map<Object,Object> getDataSourceMap() {
Map<Object, Object> map = new HashMap<>();
if (jdbc!= null && jdbc.size()>0) {
for (int i = 0; i < jdbc.size(); i++) {
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setDriverClassName(jdbc.get(i).getDriverClassName());
druidDataSource.setUrl(jdbc.get(i).getUrl());
druidDataSource.setUsername(jdbc.get(i).getUsername());
druidDataSource.setPassword(jdbc.get(i).getPassword());
druidDataSource.setDefaultReadOnly(jdbc.get(i).isDefaultReadOnly());
map.put(jdbc.get(i).getConnname(),druidDataSource);
}
}
return map;
}
@Bean
public DynamicDataSource dynamicData() {
Map<Object, Object> targetDataSources= getDataSourceMap();
DynamicDataSource dynamicDataSource = new DynamicDataSource();
dynamicDataSource.setTargetDataSources(targetDataSources);
dynamicDataSource.setDefaultTargetDataSource(targetDataSources.values().toArray()[0]);
return dynamicDataSource;
}
@Bean
public SqlSessionFactory sqlSessionFactory(DynamicDataSource dynamicData) throws Exception{
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dynamicData);
return sqlSessionFactoryBean.getObject();
}
}
DataSourceAspect 切面
@Component
@Aspect
public class DataSourceAspect {
@Before("@annotation(TargetDataSource)")
public void before(JoinPoint joinPoint){
TargetDataSource annotation = ((MethodSignature) joinPoint.getSignature()).getMethod().getAnnotation(TargetDataSource.class);
DataSourceHolder.setDatasources(annotation.value());
}
@After("@annotation(TargetDataSource)")
public void after(){
DataSourceHolder.clearDataSources();
}
}
使用的时候,将@TargetDataSource (‘DB1’)放在dao层方法上就可以
AbstractRoutingDataSource核心方法
protected DataSource determineTargetDataSource() {
Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");
Object lookupKey = this.determineCurrentLookupKey();
DataSource dataSource = (DataSource)this.resolvedDataSources.get(lookupKey);
if (dataSource == null && (this.lenientFallback || lookupKey == null)) {
dataSource = this.resolvedDefaultDataSource;
}
if (dataSource == null) {
throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");
} else {
return dataSource;
}
}