MybatisPlus 多租户+分页插件一起使用时的坑!(分页查询时Total计算不正确)

错误用法:

     添加插件时,先添加 分页插件,在添加租户插件。

 

正确用法:

        添加插件时,先添加 租户插件,在添加 分页插件。

 

原理解析:

前提知识:

        MP中所有的内置插件都必须实现 InnerInterceptor ,InnerInterceptor 中定义了拦截点。因为Mp的插件体系是这样的:

 1. MybatisPlusInterceptor:起到执行者作用,管理了所有的内置插件。它会让所有的内置插件应用到本次的sql流程中。

2. InnerInterceptor 是所有内置插件的顶级父接口,它将Executor 和 Statment的拦截点 化简成了 许多抽象方法。 内置插件想在哪里做拦截,就重写对应的抽象方法。

本文中主要关注InnerInterceptor的如下两个抽象方法:

    public interface InnerInterceptor {

    /**
     * 判断是否执行 {@link Executor#query(MappedStatement, Object, RowBounds, ResultHandler, CacheKey, BoundSql)}
     * <p>
     * 如果不执行query操作,则返回 {@link Collections#emptyList()}
     *
     * @param executor      Executor(可能是代理对象)
     * @param ms            MappedStatement
     * @param parameter     parameter
     * @param rowBounds     rowBounds
     * @param resultHandler resultHandler
     * @param boundSql      boundSql
     * @return 新的 boundSql
     */
    default boolean willDoQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
        return true;
    }

    /**
     * {@link Executor#query(MappedStatement, Object, RowBounds, ResultHandler, CacheKey, BoundSql)} 操作前置处理
     * <p>
     * 改改sql啥的
     *
     * @param executor      Executor(可能是代理对象)
     * @param ms            MappedStatement
     * @param parameter     parameter
     * @param rowBounds     rowBounds
     * @param resultHandler resultHandler
     * @param boundSql      boundSql
     */
    default void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
        // do nothing
    }
}

       以下快速过一下分页,多租户插件的作用。

分页插件运作原理

       分页插件的 willDoQuery() :根据原始业务sql ,搞出一个 统计sql (select count(*) ...) . 将查询出来的total 设置到 分页对象page中。total>0 return true; 否者return false;

        分页插件的 beforeQuery() :将limit xx,xx 拼接上 原始业务sql上。

多租户插件运作原理:

         多租户插件的 willDoQuery() :永远return true;

        多租户插件的 beforeQuery() :将 租户id 条件拼接上 业务sql中。

整体串联

       当你使用mp进行分页查询时,首先被MybatisPlusInterceptor总执行器拦截。内部根据拦截体系点 应用所有添加的mp内置插件(分页,多租户...)

问题定位

        这里假设 我们业务是需要执行如下代码

xxxMapper.page(new Page(pageNo,pageSize),queryWrapper);

如果我们当前是错误用法(即分页插件先添加,多租户插件在添加)

        那么MybatisPlusInterceptor的逻辑中就先走分页插件,再走多租户插件

         重点就在于:此时分页插件的willDoQuery() 计算total时使用的sql 就是select count(*) from table.... 就没有带上租户id,所以此时分页查询的 total 这个就计算不对!!!

                紧接着走 query.beforeQuery(executor, ms, parameter, rowBounds, resultHandler, boundSql);  这里面也没有将租户id 拼接上 业务sql ,仅仅只是为业务sql进化成了分页sql。(即加上了limit xx,xx 到sql 上面。)

                好在于,第二次循环是租户插件,租户插件的willDoPagequery() 永远return true。所以第二次循环直接走 query.beforeQuery(...) 将租户id条件 弥补式的拼接上了 分页sql 上。

               最终你才会看到分页执行时,records命中的记录没问题,但是total总数计算的不对!!

如果我们当前是正确用法(即多租户插件先添加,分页插件在添加)

        我相信大家阅读到这里,都不用我解释了。

        首先第一次循环,多租户插件 query.beforeQuery(...) 将租户条件拼接上 业务sql上。(假设sql现在:select xxxx from table where xxx=xxx and ... and 租户id=xxx

        紧接着,第二次循环应用分页插件,分页插件先走 query.willDoQuery(...) 这一次统计sql 已经有了租户id条件了,就不会出错了,计算出来的total也是正确的。 接着走 query.beforeQuery(..)就更不会出错了。

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值