一 DataSourceConfig.class
/**
* 数据源配置类
*/
@Configuration
@EnableTransactionManagement
public class DataSourceConfig {
/**
* 只写数据源
*
* @return
*/
@Bean // 声明bean
@ConfigurationProperties("spring.datasource.druid.write") // 获取配置文件属性
@Primary // 防止循环依赖
public DataSource writeDataSource() {
return DruidDataSourceBuilder.create().build();
}
/**
* 只读数据源
*
* @return
*/
@Bean
@ConfigurationProperties("spring.datasource.druid.read")
public DataSource readDataSource() {
return DruidDataSourceBuilder.create().build();
}
/**
* 动态数据源
*
* @param writeDataSource
* @param readDataSource
* @return
*/
@Bean(name = "dynamicDataSource")
public DynamicDataSource dataSource(@Qualifier("writeDataSource") DataSource writeDataSource, @Qualifier("readDataSource") DataSource readDataSource) {
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put(DataSourceType.WRITE.name(), writeDataSource);
targetDataSources.put(DataSourceType.READ.name(), readDataSource);
return new DynamicDataSource(writeDataSource, targetDataSources);
}
/**
* 根据数据源创建SqlSessionFactory
*/
@Bean
public SqlSessionFactory sqlSessionFactory(@Qualifier("dynamicDataSource") DynamicDataSource dynamicDataSource,
@Value("${mybatis.typeAliasesPackage}") String typeAliasesPackage,
@Value("${mybatis.mapperLocations}") String mapperLocations) throws Exception {
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
// 指定数据源(这个必须有,否则报错)
factoryBean.setDataSource(dynamicDataSource);
// 下边两句仅仅用于*.xml文件,如果整个持久层操作不需要使用到xml文件的话(只用注解就可以搞定),则不加
factoryBean.setTypeAliasesPackage(typeAliasesPackage);// 指定基包
factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(mapperLocations));
return factoryBean.getObject();
}
/**
* 配置事务管理器
*/
@Bean
public DataSourceTransactionManager transactionManager(DynamicDataSource dataSource) throws Exception {
return new DataSourceTransactionManager(dataSource);
}
}
二.DataSourceType
/**
* 数据源枚举类
*/
public enum DataSourceType {
/**
* 只写库
*/
WRITE,
/**
* 只读库
*/
READ
}
三DynamicDataSource.class
/**
* 动态数据源
*/
public class DynamicDataSource extends AbstractRoutingDataSource {
public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources) {
super.setDefaultTargetDataSource(defaultTargetDataSource);
super.setTargetDataSources(targetDataSources);
super.afterPropertiesSet();
}
@Override
protected Object determineCurrentLookupKey() {
return DynamicDataSourceContextHolder.getDateSoureType();
}
}
四.DynamicDataSourceContextHolder.class
/**
* 数据源切换处理
*/
public class DynamicDataSourceContextHolder
{
public static final Logger log = LoggerFactory.getLogger(DynamicDataSourceContextHolder.class);
/**
* 使用ThreadLocal维护变量,ThreadLocal为每个使用该变量的线程提供独立的变量副本,
* 所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
*/
private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();
/**
* 设置数据源的变量
*/
public static void setDateSoureType(String dsType)
{
log.info("切换到{}数据源", dsType);
CONTEXT_HOLDER.set(dsType);
}
/**
* 获得数据源的变量
*/
public static String getDateSoureType()
{
return CONTEXT_HOLDER.get();
}
/**
* 清空数据源变量
*/
public static void clearDateSoureType()
{
CONTEXT_HOLDER.remove();
}
}
五.DataSourceAspect.class
/**
* 针对带有数据源类型注解方法的切面
*/
@Aspect
@Component
@Order(1)
public class DataSourceAspect {
protected Logger logger = LoggerFactory.getLogger(getClass());
@Pointcut("@annotation(com.vesus.springbootfastdfs.annotation.DataSourceTypeAnnotation)")
public void pointCut() {
}
/**
* 环绕通知
* @param point
* @return
* @throws Throwable
*/
@Around("pointCut()")
public Object aspectPointBefor(ProceedingJoinPoint point) throws Throwable {
// 获取该切点(带该注解)的方法
MethodSignature methodSignature = (MethodSignature) point.getSignature();
Method method = methodSignature.getMethod();
// 获取真正调用的方法(serviceImpl)/**此处容易遗漏出错*/
Method relmethod = point.getTarget().getClass()
.getMethod(point.getSignature().getName(), method.getParameterTypes());
// 获取该方法(serviceImpl)上的注解
DataSourceTypeAnnotation annotation = relmethod.getAnnotation(DataSourceTypeAnnotation.class);
if (annotation != null) {
// 设置该方法的数据源类型
logger.info("数据源{}注解值", annotation.value());
DynamicDataSourceContextHolder.setDateSoureType(annotation.value());
}
try {
return point.proceed();
} finally {
// 销毁数据源 在执行方法之后
DynamicDataSourceContextHolder.clearDateSoureType();
}
}
}
六.application.yml(部分)
备注:以上是该方法核心代码,自定义注解需要自己编写;