基于 mybatis-plus 的空集合拦截器

有这么一个场景

比如说现在有两个表,一个员工表,一个部门表,员工表内含有部门 code 字段

CREATE TABLE `employee` (
  `id` bigint NOT NULL COMMENT '主键',
  `name` varchar(32) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '员工姓名',
  `dept_code` varchar(32) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '员工所属部门',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
CREATE TABLE `dept` (
  `id` bigint NOT NULL COMMENT '主键',
  `p_code` varchar(32) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '父编码',
  `code` varchar(32) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '部门编码',
  `name` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '部门名称',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;

现在需要查询某个部门下的所有员工。

由于部门大多数情况下是树状结构的,所以我们通常的做法是,先根据部门编码查询出自身及所有子部门的编码,作为部门编码集合list,然后

SELECT
	* 
FROM
	employee 
WHERE
	dept_code IN (
	"dept1",
	"dept2")

用 mybatis-plus 的写法就是

        QueryWrapper<Employee> wrapper = new QueryWrapper<>();
        // 查询所有子部门code
        List<String> list = new ArrayList<>();
        list.add("dept1");
        list.add("dept2");
        wrapper.in("dept_code", list);
        List<Employee> result = mapper.selectList(wrapper);
        System.out.println(result);

正常情况下,没有任何问题,逻辑也很简单。

但是,如果部门编码传错了,或者说没有子部门,即 list 为空,sql 相应变为了

SELECT
	* 
FROM
	employee 
WHERE
	dept_code IN ()

于是自然而然的抛出了

org.springframework.jdbc.BadSqlGrammarException

最容易想到的解决方式:

        QueryWrapper<Employee> wrapper = new QueryWrapper<>();
        // 查询所有子部门code
        List<String> list = new ArrayList<>();
        list.add("dept1");
        list.add("dept2");
        // 如果为空,说明不应该查出任何人,可以加个 1=0 的条件
        if (CollectionUtils.isEmpty(list)) {
            wrapper.eq("1", "0");
        } else {
            // 不为空则正常查询
            wrapper.in("dept_code", list);
        }
        List<Employee> result = mapper.selectList(wrapper);
        System.out.println(result);

就是如果 list 为空,则 sql 变成了

SELECT
	* 
FROM
	employee 
WHERE
	1 = 0

不会查询出任何数据,即该部门下没有任何员工,符合查询逻辑。

但是这种在 java 业务代码里添加 if else 逻辑的方式不够优雅。

想到之前看到了 mybatis-plus 的插件,于是想着能不能通过插件的方式完善该功能

直接上代码

@NoArgsConstructor
public class EmptyListPlugin implements InnerInterceptor {

    private static final Pattern PATTERN = Pattern.compile("[\"|'](.*?)[\"|']");

    @Override
    public boolean willDoQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
        return !hasEmptyList(boundSql.getSql());
    }

    /**
     * 判断是否存在空list
     */
    private static boolean hasEmptyList(String sql) {
        sql = removeInterference(sql);
        List<String> keywordList = Lists.newArrayList("in", "IN");
        boolean hasEmptyList = Boolean.FALSE;
        for (String keyword : keywordList) {
            List<String> matcherList = Lists.newArrayList();
            // 获取关键词,关键词前必须为空白字符,但以关键词开头的单词也会被匹配
            // 匹配以" in (" 开头的,")"结尾的最短字符串
            Pattern pattern = Pattern.compile("(?<=\\s" + keyword + "\\s{0,10}\\().*?(?=\\))");
            Matcher matcher = pattern.matcher(sql);
            while (matcher.find()) {
                matcherList.add(matcher.group());
            }
            if (CollectionUtils.isNotEmpty(matcherList)) {
                hasEmptyList = checkEmptyList(matcherList);
                if (hasEmptyList) {
                    break;
                }
            }
        }
        return hasEmptyList;
    }

    /**
     * 去除字符中的干扰项,避免字符串中的内容干扰判断。
     */
    private static String removeInterference(String sql) {
        Matcher matcher = PATTERN.matcher(sql);
        while (matcher.find()) {
            String replaceWord = matcher.group();
            sql = sql.replace(replaceWord, "''");
        }
        return sql;
    }

    /**
     * 校验是否有空的字符串
     */
    private static boolean checkEmptyList(List<String> matcherList) {
        boolean isHaveEmptyList = Boolean.FALSE;
        // 获取()内的内容
        for (String subSql : matcherList) {
            // 如果关键词之后无任何sql语句,则sql语句结尾为关键词,此时判定为空列表
            if (StringUtils.isBlank(subSql)) {
                isHaveEmptyList = Boolean.TRUE;
                break;
            }
        }
        return isHaveEmptyList;
    }
}

记得添加该插件到 MybatisPlusInterceptor

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        List<InnerInterceptor> list = new ArrayList<>();
        list.add(emptyListPlugin());
        interceptor.setInterceptors(list);
        return interceptor;
    }

    @Bean
    public EmptyListPlugin emptyListPlugin() {
        return new EmptyListPlugin();
    }

  • 6
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值