springboot数据源切换核心为继承 AbstractRoutingDataSource 重写 determineCurrentLookupKey
集成步骤
1.多数据源配置类
@Configuration
public class DataSourceConfig {
// @Primary
// @Bean("sqlSessionFactory2")
// public SqlSessionFactory sqlSessionFactoryBean(DynamicDataSource dynamicDataSource) throws Exception {
// SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
// sqlSessionFactoryBean.setDataSource(dynamicDataSource);
// sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:/mapper/*.xml"));
// org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
// configuration.setMapUnderscoreToCamelCase(true);
// configuration.setDefaultFetchSize(100);
// configuration.setDefaultStatementTimeout(30);
// sqlSessionFactoryBean.setConfiguration(configuration);
// return sqlSessionFactoryBean.getObject();
// }
private DataSourceNewCrmProp newCrmProp;
private DataSourceEbCrmProperties ebCrmProperties;
@Bean("master")
@ConditionalOnClass(DataSourceEbCrmProperties.class)
public DataSource masterDataSource() {
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setUrl(ebCrmProperties.getUrl());
druidDataSource.setUsername(ebCrmProperties.getUsername());
druidDataSource.setPassword(ebCrmProperties.getPassword());
druidDataSource.setDriverClassName(ebCrmProperties.getDriverClassName());
return druidDataSource;
}
@Bean("slave")
@ConditionalOnClass(DataSourceNewCrmProp.class)
public DataSource slaveDataSource() {
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setUrl(newCrmProp.getUrl());
druidDataSource.setUsername(newCrmProp.getUsername());
druidDataSource.setPassword(newCrmProp.getPassword());
druidDataSource.setDriverClassName(newCrmProp.getDriverClassName());
return druidDataSource;
}
@Bean
@Primary
public DynamicDataSource dynamicDataSource() {
Map<Object, Object> map = new HashMap<>();
map.put(DataSourceConstants.EB_CRM, masterDataSource());
map.put(DataSourceConstants.NEW_CRM, slaveDataSource());
return new DynamicDataSource(masterDataSource(), map);
}
@Autowired
public void setNewCrmProp(DataSourceNewCrmProp newCrmProp) {
this.newCrmProp = newCrmProp;
}
@Autowired
public void setEbCrmProperties(DataSourceEbCrmProperties ebCrmProperties) {
this.ebCrmProperties = ebCrmProperties;
}
}
2.集成重写AbstractRoutingDataSource
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DataSourceHolder.getDataSource();
}
/**
* 构造方法填充Map,构建多数据源
*/
public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources) {
//默认的数据源,可以作为主数据源
super.setDefaultTargetDataSource(defaultTargetDataSource);
//目标数据源
super.setTargetDataSources(targetDataSources);
//执行afterPropertiesSet方法,完成属性的设置
super.afterPropertiesSet();
}
}
3.结合ThreadLocal完成切换
public class DataSourceHolder {
//线程 本地环境
private static final ThreadLocal<String> dataSources = new InheritableThreadLocal();
//设置数据源
public static void setDataSource(String datasource) {
dataSources.set(datasource);
}
//获取数据源
public static String getDataSource() {
return dataSources.get();
}
//清除数据源
public static void clearDataSource() {
dataSources.remove();
}
}
4.扩展 配合AOP实现自定义
@Aspect
@Component
public class DataSourceAspect {
@Pointcut("execution(* com.ebei.syn.service.impl.yscrm.*..*(..))")
public void dataSourcePointCut() {
}
@Around("dataSourcePointCut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
ChangeDataSource dsAnnotation = getDSAnnotation(joinPoint);
if (Objects.nonNull(dsAnnotation)) {
String dsKey = dsAnnotation.value();
DataSourceHolder.setDataSource(dsKey);
}
try {
return joinPoint.proceed();
} finally {
DataSourceHolder.clearDataSource();
}
}
/**
* 根据类或方法获取数据源注解
*/
private ChangeDataSource getDSAnnotation(ProceedingJoinPoint joinPoint) {
Class<?> targetClass = joinPoint.getTarget().getClass();
if (targetClass.isAnnotationPresent(ChangeDataSource.class)) {
return targetClass.getAnnotation(ChangeDataSource.class);
}
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
if (methodSignature.getMethod().isAnnotationPresent(ChangeDataSource.class)) {
return methodSignature.getMethod().getAnnotation(ChangeDataSource.class);
}
return null;
}
}
@Documented
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(value = RetentionPolicy.RUNTIME)
public @interface ChangeDataSource {
/**
* 数据源名-默认为new-crm,提供给定时任务同步数据源切换
*/
String value() default DataSourceConstants.NEW_CRM;
}