目前做的项目有一个需求就是,可以动态地查数据,能够在前端页面配置添加数据源,然后将数据源与业务进行绑定,当查询数据的时候并不是固定拿取yml配置文件中配置死的数据库配置,而是根据与业务绑定存在数据库表中的数据源进行连接。
虽然若依已经做过数据源切换的封装了,通过AOP的方式去指定数据源,但是若依原本的只能是切换配置文件中已有的数据源,意味着如果我要再加一个新的数据源就需要去配置文件里加上,修改也是如此,这样显然很麻烦。不符合我们的需求。
/**
* 自定义多数据源切换注解
*
* 优先级:先方法,后类,如果方法覆盖了类上的数据源类型,以方法的为准,否则以类上的为准
*
* @author ruoyi
*/
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface DataSource
{
/**
* 切换数据源名称
*/
public DataSourceType value() default DataSourceType.MASTER;
}
然后我的思路是在若依原有的基础上再加一层处理,将原本若依编写的环绕通知注释掉,使用@Before()前置通知做处理
在数据源枚举中加入一个新的枚举OTHER来标识切换数据源。
/**
* 数据源
*
* @author ruoyi
*/
public enum DataSourceType
{
/**
* 主库
*/
MASTER,
/**
* 从库
*/
SLAVE,
/**
* 其他
*/
OTHER
}
在切面前置通知中对指定的类型进行判断,如果是master则使用默认的数据源,slave则使用配置文件中配置的从库,other则表示使用存在数据库表中的数据源,随后会根据请求方法的参数获取业务的ID然后去数据库查询与之绑定的数据源信息,然后将之设置成当前数据源,使用完后通过后置通知@After将数据源清除掉。
/**
* 多数据源处理
*
* @author ruoyi
*/
@Log4j2
@Aspect
@Order(1)
@Component
public class DataSourceAspect
{
protected Logger logger = LoggerFactory.getLogger(getClass());
@Resource
private ISysDataSourceService sysDataSourceService;
@Pointcut("@annotation(com.ruoyi.common.annotation.DataSource)"
+ "|| @within(com.ruoyi.common.annotation.DataSource)")
public void dsPointCut()
{
}
@Before("dsPointCut()")
public void doBefore(JoinPoint joinPoint) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
logger.info("方法名称: " + method.getName());
DataSource dataSource = method.getAnnotation(DataSource.class);
DruidConfig dynamicDataSourceConfig = SpringContextUtils.getBean(DruidConfig.class);
Map<Object, Object> map = dynamicDataSourceConfig.getTargetDataSources();
log.info("TargetDataSources==="+map.toString());
// 通过判断 DataSource 中的值来判断当前方法应用哪个数据源
String value = String.valueOf(dataSource.value());
boolean flag;
String name = "";
switch (value) {
case "MASTER":
flag = true;
name = DataSourceType.MASTER.name();
break;
case "SLAVE":
flag = true;
name = DataSourceType.SLAVE.name();
break;
case "OTHER":
flag = true;
name = "OTHER";
if (map.get(name) != null) {
break;
} else {
//获取传入参数
Object[] args = joinPoint.getArgs();
JSONObject json = (JSONObject) args[0];
Long dataSourceId = json.getLong("dataSourceId");
log.info("获取的数据源ID=="+dataSourceId);
//从传入参数获取业务ID查找数据源信息,设置数据源信息
SysDataSource source = sysDataSourceService.getById(dataSourceId);
String url = source.getDbUrl();
String username = source.getDbUsername();
//密码解密
String password = SecurityUtil.jiemi(source.getDbPwd());
log.info("解密后密码=="+password);
log.info("数据源切换:" + url);
DruidDataSource s = dynamicDataSourceConfig.createDataSource(name, url, username,
password, source.getDbDriver());
}
break;
default:
flag = false;
break;
}
if (!flag) {
logger.error("************注意************");
name = DataSourceType.MASTER.name();
logger.info("加载到未知数据源,系统自动设置数据源为默认数据源!");
}
DynamicDataSource.setDataSource(name);
//设置成数据源
DynamicDataSourceContextHolder.setDataSourceType(name);
logger.info("当前数据源: " + name);
logger.info("当前数据源: " + ((DruidDataSource) map.get(name)).getUrl());
logger.info("set datasource is " + name);
}
@After("dsPointCut()")
public void doAfter() {
logger.info("*********准备清除数据源*********");
DynamicDataSource.clearDataSource();
DynamicDataSourceContextHolder.clearDataSourceType();
logger.info("*********数据源清除成功*********");
}
需要注意的是在druidConfig的配置文件中加上新添的枚举类型
当需要切换数据源时,只需在类上或者方法上加上注解
@DataSource(value = DataSourceType.OTHER)