springboot+mybatis拦截器+自定义注解实现业务数据权限控制、逻辑删除,分页查询存在问题修改

springboot+mybatis拦截器+自定义注解实现业务数据权限控制、逻辑删除
当【指定查询字段】且【分页查询】时,查询过滤存在问题:查询字段必须包含【deleteFlag】和【权限字段】,否则分页计数SQL会报错。
分页查询SQL如下:

SELECT COUNT(*) FROM (SELECT 指定字段1,指定字段2 FROM 表名) TOTAL

在经过逻辑删除过滤和业务数据权限过滤方法的修改后,SQL语句会变成:

SELECT COUNT(*) FROM (SELECT 指定字段1,指定字段2 FROM 表名) TOTAL WHERE TOTAL.delete_flag = ‘0’ AND TOTAL.权限字段 = ‘xxx’

因为TOTAL中不存在【deleteFlag】和【权限字段】,所以会报错。

解决思路:

  1. 计数 SQL 应该把过滤条件写在子查询内部,而不是加在外层。
  2. 因此拦截器里必须判断:当前 SQL 是不是“分页计数”场景,如果是,就把条件塞进子查询 () 内部;否则保持原逻辑即可。

代码实现:

修改查询过滤(select)方法
    public Object select(Invocation invocation) throws Throwable {
        StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
        MetaObject metaObject = SystemMetaObject.forObject(statementHandler);
        MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
        // 获取当前查询的实体类类型
        Class<?> clazz = getEntityClass(mappedStatement);
        if (clazz == null) {
            return invocation.proceed();
        }

        // 解析和修改 SQL
        BoundSql boundSql = statementHandler.getBoundSql();
        String originalSql = boundSql.getSql();
        try {
            Statement statement = CCJSqlParserUtil.parse(originalSql);
            if (statement instanceof Select) {
                Select selectStatement = (Select) statement;
                SelectBody selectBody = selectStatement.getSelectBody();
                if (selectBody instanceof PlainSelect) {
                    PlainSelect ps = (PlainSelect) selectBody;
                    //---------------------------------------- 修改开始 ----------------------------------------
                    // 判断是否是分页计数查询
                    if (isPageCountSql(ps)) {
                        // 分页计数 SQL,把条件塞进子查询
                        SubSelect subSelect = (SubSelect) ps.getFromItem();
                        SelectBody innerSelectBody = subSelect.getSelectBody();
                        if (innerSelectBody instanceof PlainSelect) {
                            PlainSelect innerPs = (PlainSelect) innerSelectBody;
                            String innerAlias = innerPs.getFromItem().getAlias() == null ? null : innerPs.getFromItem().getAlias().getName();

                            Expression innerWhere = innerPs.getWhere();

                            innerWhere = addDeleteFlag(innerAlias, innerPs, innerWhere, clazz);
                            innerWhere = addBusinessDataAuto(innerAlias, innerPs, innerWhere, clazz);

                            // 子查询已经包含过滤条件,外层无需再过滤
                            ps.setWhere(null);
                        }
                    } else {
                        // 普通查询,按原逻辑处理
                        String aliasName = ps.getFromItem().getAlias() == null ? null : ps.getFromItem().getAlias().getName();
                        Expression where = ps.getWhere();

                        where = addDeleteFlag(aliasName, ps, where, clazz);
                        where = addBusinessDataAuto(aliasName, ps, where, clazz);
                    }
                    //---------------------------------------- 修改结束 ----------------------------------------
                    
                    // 使用反射更新 BoundSql 中的 SQL
                    updateBoundSqlSql(boundSql, selectStatement.toString());
                }
            }
        } catch (Exception e) {
            throw new SQLException("解析SQL失败!\n" +
                    "SQL:" + originalSql, e);
        }
        return invocation.proceed();
    }


    /**
     * 判断是否是分页计数 SQL:
     * 最外层是 SELECT COUNT(*) FROM (子查询) TOTAL
     */
    public boolean isPageCountSql(PlainSelect plainSelect) {
        // 只有一项 selectItem 且是 COUNT(*)
        if (plainSelect.getSelectItems().size() != 1) {
            return false;
        }
        String selectItem = plainSelect.getSelectItems().get(0).toString();
        if (!selectItem.matches("(?i)COUNT\\(\\s*\\*\\s*\\)")) {
            return false;
        }

        // FROM 是一个子查询
        if (!(plainSelect.getFromItem() instanceof net.sf.jsqlparser.statement.select.SubSelect)) {
            return false;
        }

        // 子查询别名是 TOTAL
        net.sf.jsqlparser.statement.select.SubSelect subSelect =
                (net.sf.jsqlparser.statement.select.SubSelect) plainSelect.getFromItem();
        Alias alias = subSelect.getAlias();
        return alias != null && "TOTAL".equalsIgnoreCase(alias.getName());
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值