当前使用版本(必填,否则不予处理)

3.4.3.4 (latest)

该问题是如何引起的?(确定最新版也有问题再提!!!)

现象: 多租户插件和分页插件同时配置的时候,分页的结果数据总记录数统计没有进行租户字段过滤,分页数据集合有进行租户字段过滤。

重现步骤(如果有就写完整)

代码片段:

//插件配置
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        //分页插件
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        //阻止全表更新和删除插件
        interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());
        //多租户插件
        interceptor.addInnerInterceptor(new TenantLineInnerInterceptor(new MyTenantLine()));
        return interceptor;
    }

    //行级租户处理器
    public static class MyTenantLine implements TenantLineHandler{

        private static ThreadLocal<Long> tenantId=new ThreadLocal<>();

        public static void setTenantId(Long id){
            tenantId.set(id);
        }

        /**
         * 获取租户唯一标识,每次查询都从这里获取,然后进行过滤,
         * 可从每次请求前端传入,这里模拟使用ThreadLocal获取
         */
        @Override
        public Expression getTenantId() {
            return new LongValue(tenantId.get());
        }

        //获取租户唯一标识字段名,默认是tenant_id
        @Override
        public String getTenantIdColumn() {
            return TenantLineHandler.super.getTenantIdColumn();
        }

        //控制哪些表需要过滤租户字段,哪些不需要过滤租户字段
        @Override
        public boolean ignoreTable(String tableName) {
            return TenantLineHandler.super.ignoreTable(tableName);
        }
    }

执行sql打印:

==>  Preparing: SELECT COUNT(*) FROM t_user WHERE deleted_at IS NULL   -- 数据总记录统计查询sql
==>  Preparing: SELECT id, name, password, created_at, deleted_at, gids, status FROM t_user WHERE deleted_at IS NULL AND tenant_id = 1 ORDER BY created_at DESC LIMIT ?    --  数据集合查询sql

结论: 数据总记录统计查询sql没有拼接上租户字段的过滤sql。

报错信息

从源码debug中查到以下方法可能有问题:

package com.baomidou.mybatisplus.extension.plugins.inner;

public class PaginationInnerInterceptor implements InnerInterceptor {
      /**
     * 这里进行count,如果count为0这返回false(就是不再执行sql了)
     */
    @Override
    public boolean willDoQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
        IPage<?> page = ParameterUtils.findPage(parameter).orElse(null);
        if (page == null || page.getSize() < 0 || !page.isSearchCount()) {
            return true;
        }

        BoundSql countSql;
        MappedStatement countMs = buildCountMappedStatement(ms, page.countId());
        if (countMs != null) {
           /**todo 这里解析得到原始sql*/
            countSql = countMs.getBoundSql(parameter);
        } else {
            countMs = buildAutoCountMappedStatement(ms);
           /**todo 这里解析得到统计数据记录的sql:  select COUNT(*) .....*/
            String countSqlStr = autoCountSql(page.optimizeCountSql(), boundSql.getSql());
            PluginUtils.MPBoundSql mpBoundSql = PluginUtils.mpBoundSql(boundSql);
           /**todo 这里重新构建出解析成数据统计后的sql:select COUNT(*) 的BoundSql对象*/
            countSql = new BoundSql(countMs.getConfiguration(), countSqlStr, mpBoundSql.parameterMappings(), parameter);
            PluginUtils.setAdditionalParameter(countSql, mpBoundSql.additionalParameters());
        }

        CacheKey cacheKey = executor.createCacheKey(countMs, parameter, rowBounds, countSql);
        /**todo 这里直接用executor进行查询,并没有进行租户字段处理的拦截,有问题!*/
        Object result = executor.query(countMs, parameter, rowBounds, resultHandler, cacheKey, countSql).get(0);
        page.setTotal(result == null ? 0L : Long.parseLong(result.toString()));
        return continuePage(page);
    }
}



Comment From: miemieYaho

看文档

Comment From: kim709394

使用多个功能需要注意顺序关系,建议使用如下顺序

多租户,动态表名 分页,乐观锁 sql性能规范,防止全表更新与删除 总结: 对sql进行单次改造的优先放入,不对sql进行改造的最后放入

https://mp.baomidou.com/guide/interceptor.html#mybatisplusinterceptor