解决动态数据源切换问题

问题描述:

做合同到期自动发送消息的需求时,需要定时扫描所有的数据源信息,涉及到数据源切换的实现。当时一直无法解决的原因是不理解通用代码中远程调用feign指定数据源的原理,也没有尝试去解读源码,一直在做表面分析,导致找不到解决方法,现整理思路如下:


原因分析:

原始的参考代码如下:

// 远程调用feign指定数据源.
        MockHttpServletRequest request = new MockHttpServletRequest();
        request.setAttribute(DynamicConstant.SOURCES_ID, DynamicDataSourceContextHolder.peek());
        request.setAttribute(DynamicConstant.GATEWAY_MASTER, DynamicConstant.DYNAMIC_CLEAR);
        RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request));

分析:理解MockHttpServletRequest 是用于模拟HTTP请求的一个方法类
向请求中设置参数key为"sources_id",value为DynamicDataSourceContextHolder.peek()即动态数据源队列中的sourceId值,因此这里在使用这个方法时应该传入对应的数据源的sources_id值。并设置主网关为清空状态,体现在项目中即从主数据源切换到指定数据源
在这里插入图片描述
public static final String DYNAMIC_CLEAR = “clear”;
public static final String GATEWAY_MASTER = “gateway_master”;

这里远程调用feign指定数据源就可以封装成一个方法(参数为sourceId),供后面的代码中远程调用服务去使用

使用方式:setSourcesId(sourcesId);在远程调用代码执行之前进行设置,
注意:当需要再次远程调用时需要重新设置,每次都要指定远程数据源
注意:由于切换数据源的内部拼接sql需要去查询主数据源的数据源配置表,因此在执行此方法之前必须清除当前数据源信息(使线程处于主数据源)

private void setSourcesId(String sourcesId) {
        MockHttpServletRequest request = new MockHttpServletRequest();
        // System.out.println(DynamicDataSourceContextHolder.peek());
        request.setAttribute(DynamicConstant.SOURCES_ID, sourcesId);
        request.setAttribute(DynamicConstant.GATEWAY_MASTER, DynamicConstant.DYNAMIC_CLEAR);
        RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request));
    }

当定时任务被触发时,首先清除当前的数据源信息
DynamicDataSourceContextHolder.clear();
在主数据源下查询数据源配置表,遍历(循环中首先清除数据源),指定数据源,处理业务逻辑(业务逻辑方法参数传入指定数据源的sourceId,用于远程调用服务前)

分析:指定数据源的实现思路

public boolean swatchByMark(String mark) {

        if (StrUtil.isEmpty(mark)) {
            return false;
        }

        String executeSql = StrUtil.format(DynamicConstant.DEFAULT_DB_TENANT, mark);
        Triple<String, String, String> dbInfo = getDbInfo(executeSql);

        if (dbInfo.getLeft() == null || dbInfo.getMiddle() == null || dbInfo.getRight() == null) {
            return false;
        }

        // 切换数据源
        return swatch(dbInfo.getRight());
    }

public static final String DEFAULT_DB_TENANT = “SELECT id,mark,sources_id FROM sys_tenant where mark=‘{}’ limit 1”;
这里写了一个拼接的sql,调用getDbInfo方法获取到三个查询结果,将第三个结果即sourceId传入,swatch()方法的作用是校验数据源是否存在并进行切换,最终调用的还是DynamicDataSourceContextHolder.push(sourceId);对数据源进行设置

public Triple<String, String, String> getDbInfo(String sql) {

        Map<String, Object> result = jdbcTemplate.queryForMap(sql);

        if (ObjectUtil.isEmpty(result) || result.isEmpty()) {
            return Triple.of(null, null, null);
        }

        String id = String.valueOf(result.get("id"));
        String mark = String.valueOf(result.get("mark"));
        String sourceId = String.valueOf(result.get("sources_id"));

        return Triple.of(id, mark, sourceId);
    }

拓展知识点:

对于在循环中可能会出现异常的代码行,可以采用try-catch的方式捕获异常信息,并continue,这样可以结束本次循环,执行下一循环

// 查询表Employee合同到期的雇员
        List<EmployeeContractVO> employeeContractVOs;
;        try {
            employeeContractVOs = employeeService.selectByContract();
        }catch (Exception e){
            e.printStackTrace();
            log.info("查询数据库合同信息出现错误,重试下一个");
            continue;
        }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值