由于需要做多数据源切换,然后想到去年做过一次多数据源切换,但是之前是springmvc的,现在是springboot,两者有所不同,在此做个简单的记录。
第一步还是一样的,写一个DynamicDataSourceHolder,用来获取需要使用的数据源是哪个。
public class DynamicDataSourceHolder {
private static final ThreadLocal<String> THREAD_DATA_SOURCE = new ThreadLocal<>();
public static String getDataSource() {
return THREAD_DATA_SOURCE.get();
}
public static void setDataSource(String dataSource) {
THREAD_DATA_SOURCE.set(dataSource);
}
}
第二步再写一个DynamicDataSource类继承AbstractRoutingDataSource,并重写determineCurrentLookupKey方法。
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DynamicDataSourceHolder.getDataSource();
}
}
第三步准备两个数据源(仅做演示效果,大家根据实际情况更改)
spring:
datasource:
fop-data:
url:
username:
password:
big-data:
url:
username:
password:
将数据源注册到spring中
@Bean
@ConfigurationProperties(prefix = "spring.datasource.fop-data")
public DruidDataSource dataSource1() throws SQLException {
DruidDataSource druidDataSource = new DruidDataSource();
return setDruidDataSourceOther(druidDataSource);
}
@Bean
@ConfigurationProperties(prefix = "spring.datasource.big-data")
public DruidDataSource dataSource2() throws SQLException {
DruidDataSource druidDataSource = new DruidDataSource();
return setDruidDataSourceOther(druidDataSource);
}
第四步配置多数据源,需要注意的是:@Primary一定要加这里,缺失@Primary会导致报错,如果加到其他数据源(如dataSource1)上,会无法切换。
@Primary
@Bean
public DynamicDataSource dynamicDataSource() throws SQLException {
DynamicDataSource dataSource = new DynamicDataSource();
Map<Object, Object> targetDataSources = new HashMap<>(16);
targetDataSources.put("dataSource1",dataSource1());
targetDataSources.put("dataSource2",dataSource2());
dataSource.setTargetDataSources(targetDataSources);
// 默认数据源
dataSource.setDefaultTargetDataSource(dataSource1());
return dataSource;
}
放个缺失@Primary的异常:
Action:
Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or using @Qualifier to identify the bean that should be consumed
ps:起初我随便加了bean的位置,然后切不了…
第五步添加事务管理
@Bean
public PlatformTransactionManager transactionManager() throws SQLException {
return new DataSourceTransactionManager(dynamicDataSource());
}
补充下DruidDataSource数据源剩下的代码:
private DruidDataSource setDruidDataSourceOther(DruidDataSource druidDataSource) throws SQLException {
druidDataSource.setInitialSize(5);
druidDataSource.setDriverClassName("com.mysql.jdbc.Driver");
druidDataSource.setMinIdle(5);
druidDataSource.setMaxActive(30);
druidDataSource.setMaxWait(60000);
druidDataSource.setTimeBetweenEvictionRunsMillis(60000);
druidDataSource.setMinEvictableIdleTimeMillis(300000);
druidDataSource.setTestWhileIdle(true);
druidDataSource.setTestOnBorrow(false);
druidDataSource.setTestOnReturn(false);
druidDataSource.setFilters("stat");
return druidDataSource;
}
到这一步已经可以使用了,在访问数据前使用 DynamicDataSourceHolder.setDataSource() 做数据源切换。
下面我们使用AOP + 注解的方式对数据源进行切换:
首先定义注解:
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface DataSource {
DataSourceType value();
}
枚举:
public enum DataSourceType {
FOP_DATA("dataSource1"),
BIG_DATA("dataSource2");
public String getValue() {
return value;
}
private String value;
DataSourceType(String value){
this.value = value;
}
}
在需要切换数据源的方法上加上注解,如图:
再加上AOP切面:
@Component
@Aspect
public class DataSourceAOP {
@Before("@annotation(source)")
public void intercept(JoinPoint point,DataSource source){
DynamicDataSourceHolder.setDataSource(source.value().getValue());
}
}
这个时候加了注解的可以了,但是还有个问题,当切换数据源之后,没有加注解的也使用了切换之后的数据源了,所以我又加了个默认的数据源
/**
* 没有加注解的默认数据源为 FOP_DATA
*/
@Before("execution(* com.obanks.batch.job.executor.service.*.*(..))")
public void defIntercept(){
DynamicDataSourceHolder.setDataSource(DataSourceType.FOP_DATA.getValue());
}
更:
不好意思,漏了个地方,还有启动类上需要排除 数据源自动配置类
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
有疑问还请评论区留言~
END