baomidou / mybatis-plus

An powerful enhanced toolkit of MyBatis for simplify development
https://baomidou.com
Apache License 2.0
16.28k stars 4.29k forks source link

IllegalSQLInnerInterceptor 和 BaseMapper.selectCount(Wrapper)+逻辑删除 冲突问题 #6324

Open zevinc opened 2 months ago

zevinc commented 2 months ago

版本:3.5.6

问题描述:调用 xxxMapper.selectCount(Wrappers.lambdaQuery())方法, where语句拼接了逻辑删除字段并且启用IllegalSQLInnerInterceptor会报错。

错误信息:非法SQL,SQL未使用到索引,table: xxx,columnName:xxx。

计划执行sql语句:SELECT COUNT(*) AS total FROM xxx WHERE del = 0

我的需求:公司规定的公共字段(包括逻辑删除字段)没有使用索引,并且selectCount()方法是mp自身提供的方法,如何在不修改表schema、启用IllegalSQLInnerInterceptor插件的情况下正常执行不报错。

我在官方文档中看到【手动设置拦截器忽略执行策略】,就是以下方法:InterceptorIgnoreHelper.handle(IgnoreStrategy.builder().illegalSql(true).build()); 但是它有很多缺点: 1、每次都要手动调用,并且调用mapper方法还不是自定义的方法,而是官方提供的方法(baseMapper.selectCount()),能否在与IllegalSQLInnerInterceptor该插件有冲突的mapper方法中进行有效处理,比如在baseMapper.selectCount()内部检测该插件是否启用然后进行自动关闭,或者在IllegalSQLInnerInterceptor插件中获取当前调用mapper方法进行自动忽略,而不是交由开发者去处理该类问题(如果是自定义注入的mapper方法我会尝试自己解决) 2、当我手动调用拦截器忽略时,可能会执行多个mapper方法,可能只有部分执行需要被忽略,这导致每个方法执行都要判断是否需要手动忽略,然后在方法前后增加InterceptorIgnoreHelper.handle()和clearIgnoreStrategy(),这显然很麻烦,尤其是插件主体设计者和调用mapper方法不是同一个人的情况下,往往为了解决BUG被迫新增手动忽略插件方法,导致接口开发人员产生抱怨(基于公司规范,自定义了一系列InnerInterceptor) 3、当我使用其他插件时会对SQL的where条件进行处理,可能新增或修改where条件的字段,但是它不是索引,能否通过字段名称或其他规则跳过IllegalSQLInnerInterceptor.validWhere()方法,我尝试二次修改该插件,但是部分方法是不对外公开的。

nieqiurong commented 2 months ago
        var mybatisPlusInterceptor = new MybatisPlusInterceptor();
        mybatisPlusInterceptor.addInnerInterceptor(new IllegalSQLInnerInterceptor() {
            @Override
            public void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) {
                PluginUtils.MPStatementHandler mpStatementHandler = PluginUtils.mpStatementHandler(sh);
                MappedStatement ms = mpStatementHandler.mappedStatement();
                if (ms.getId().endsWith("selectCount")) {
                    return;
                }
                super.beforePrepare(sh, connection, transactionTimeout);
            }
        });
Todd-start commented 2 months ago

3.5.7版本也有这个问题 where语句会把逻辑删除字段拼写到sql的where后的第一个位置。导致SQL检查索引时报错。

nieqiurong commented 2 months ago

框架不好做的既要又要的情况,如果在没传条件的时候,就一个普通逻辑删除字段,那分析出来确实是没走索引,目前这插件其实也没多少用,自己按自己场景写一个适配的好点。

Todd-start commented 2 months ago

框架不好做的既要又要的情况,如果在没传条件的时候,就一个普通逻辑删除字段,那分析出来确实是没走索引,目前这插件其实也没多少用,自己按自己场景写一个适配的好点。

这边是传了其他的where条件的。where sql 会拼成 where deleted = ? (and otherCondition = ?) 这个样子。可能这俩顺序反一下更合理一些。

nieqiurong commented 2 months ago

框架不好做的既要又要的情况,如果在没传条件的时候,就一个普通逻辑删除字段,那分析出来确实是没走索引,目前这插件其实也没多少用,自己按自己场景写一个适配的好点。

这边是传了其他的where条件的。where sql 会拼成 where deleted = ? (and otherCondition = ?) 这个样子。可能这俩顺序反一下更合理一些。

我有空跟进看看.

nieqiurong commented 2 months ago

框架不好做的既要又要的情况,如果在没传条件的时候,就一个普通逻辑删除字段,那分析出来确实是没走索引,目前这插件其实也没多少用,自己按自己场景写一个适配的好点。

这边是传了其他的where条件的。where sql 会拼成 where deleted = ? (and otherCondition = ?) 这个样子。可能这俩顺序反一下更合理一些。

这里我测试了,这里确实存在问题,不过改动起来也不是简单的把delete放置最后能解决的,这里进行语法分析还是得提取字段来进行.