需求驱动技术发展
最近有公司项目有一个动态切换数据源的需求,但是网上找到的多半不能用或者是功能不全,自己摸索出了最终配置,在此记录下,帮助下有同样需求的人。
不墨迹直接贴代码
我的项目是分层项目,我选择在Service层写动态切换,因为可以在业务层随意切换数据源,很方便
首先定义AbstractRoutingDataSource,这个类是Spring为我们提供的用来动态切换数据源的路由基类,我们只需要实现它并且重写determineCurrentLookupKey方法即可,目的是在Spring选择数据源的时候由我们来动态的指定数据源的Key。
DynamicDataSource :
import org.springframework.beans.factory.InitializingBean;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
/**
* AbstractRoutingDataSource 类为Spring提供的多数据源路由的基类
* 继承此类实现determineCurrentLookupKey方法返回一个数据源的Key即可动态的切换数据源
*
* @author wangjie-pc
*/
public class DynamicDataSource extends AbstractRoutingDataSource implements InitializingBean {
/**
* 取得当前使用哪个数据源
*
* @return
*/
@Override
protected Object determineCurrentLookupKey() {
return DbContextHolder.getDbType();
}
}
然后创建 DbContextHolder 数据源切换器,数据源切换器的目的是使用ThreadLocal来存储我们当前线程的数据源,实现不同线程不干扰,这点很重要,并且在切换数据源的时候动态的切换分页方言,因为项目使用的Mybatis-plus,可能切换方式有所不同,各位自行查询自己使用的持久层框架如何切换吧
DbContextHolder:
import com.alibaba.druid.pool.DruidDataSource;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import com.tties.prepaid.enums.DBTypeEnum;
import com.tties.prepaid.service.SpringContextBeanService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import java.lang.reflect.Method;
/**
* 切换数据源Holder
*
* @author wangjie-pc
*/
@Slf4j
public class DbContextHolder {
private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();
/**
* 设置数据源
*
* @param dbTypeEnum
*/
public static void setDbType(DBTypeEnum dbTypeEnum) {
//切换数据源
CONTEXT_HOLDER.set(dbTypeEnum.getValue());
//切换分页插件方言
try {
DynamicDataSource dynamicDataSource = SpringContextBeanService.getBean(DynamicDataSource.class);
Method determineTargetDataSourceMethod = BeanUtils.findDeclaredMethod(AbstractRoutingDataSource.class, "determineTargetDataSource");
determineTargetDataSourceMethod.setAccessible(true);
String type = ((DruidDataSource) determineTargetDataSourceMethod.invoke(dynamicDataSource)).getDbType();
//切换分页方言
SpringContextBeanService.getBean(PaginationInterceptor.class).setDialectType(type);
} catch (Exception e) {
log.error("Dialect Switching Failed!", e);
}
}
/**
* 取得当前数据源
*
* @return
*/
public static String getDbType() {
return CONTEXT_HOLDER.get();
}
/**
* 清除上下文数据
*/
public static void clearDbType() {
CONTEXT_HOLDER.remove();
}
}
接下来是重头戏MybatisPlusConfig,在这个类里面我们会配置数据源、多数据源管理器、事务
MybatisPlusConfig:
im