pagehelper分页查询明明下一页没有数据了却还是返回了数据

Java
225
0
0
2024-02-27
标签   MyBatis

场景复现

数据库初始化有9条记录。当我通过分页插件去查询数据库时,查询第2页,每页10条记录时,查询的结果竟然有9条数据。结果显然不合理,因为我查询第2页,按照逻辑应该查询第11-20条记录,因此不存在,所以返回为空,但是现在却返回9条记录。

疑问如下:

  • 为什么返回数据???
  • 为什么返回9条数据???

解决办法

pagehelper:
#  helperDialect: mysql
  reasonable: false   # 禁用合理化时,如果pageNum<1或pageNum>pages会返回空数据

源码分析

源码跟踪

直接定位到PageInterceptor的intercept方法

@Override
    public Object intercept(Invocation invocation) throws Throwable {
        try {
        
          
            //省略内容,省略内容,省略内容
 
            List resultList;
            //步骤1:调用方法判断是否需要进行分页,如果不需要,直接返回结果
            if (!dialect.skip(ms, parameter, rowBounds)) {
                //判断是否需要进行 count 查询
                if (dialect.beforeCount(ms, parameter, rowBounds)) {
                    //步骤2:查询总条数
                    Long count = count(executor, ms, parameter, rowBounds, resultHandler, boundSql);
                    //处理查询总数,返回 true 时继续分页查询,false 时直接返回
                    //步骤3:保存总条数
                    if (!dialect.afterCount(count, parameter, rowBounds)) {
                        //当查询总数为 0 时,直接返回空的结果
                        return dialect.afterPage(new ArrayList(), parameter, rowBounds);
                    }
                }
               //步骤4:执行分页查询
                resultList = ExecutorUtil.pageQuery(dialect, executor,
                        ms, parameter, rowBounds, resultHandler, boundSql, cacheKey);
            } else {
                //rowBounds用参数值,不使用分页插件处理时,仍然支持默认的内存分页
                resultList = executor.query(ms, parameter, rowBounds, resultHandler, cacheKey, boundSql);
            }
            //步骤5:封装结果
            return dialect.afterPage(resultList, parameter, rowBounds);
        } finally {
            if(dialect != null){
                dialect.afterAll();
            }
        }
    }

我们看步骤3,保存总条数,总条数会保存到ThreadLocal的Page对象中,如图代码所示

//AbstractHelperDialect的afterCount方法
public boolean afterCount(long count, Object parameterObject, RowBounds rowBounds) {
        Page page = getLocalPage();
        //(重点,重点,重点)把count保存到page对象中
        page.setTotal(count);
        if (rowBounds instanceof PageRowBounds) {
            ((PageRowBounds) rowBounds).setTotal(count);
        }
        //pageSize < 0 的时候,不执行分页查询
        //pageSize = 0 的时候,还需要执行后续查询,但是不会分页
        if (page.getPageSize() < 0) {
            return false;
        }
        return count > ((page.getPageNum() - 1) * page.getPageSize());
    }

重点来了,我们跟进Page的setTotal方法

//Page###setTotal
public void setTotal(long total) {
        this.total = total;
        if (total == -1) {
            pages = 1;
            return;
        }
        if (pageSize > 0) {
            pages = (int) (total / pageSize + ((total % pageSize == 0) ? 0 : 1));
        } else {
            pages = 0;
        }
        //分页合理化,针对不合理的页码自动处理
        if ((reasonable != null && reasonable) && pageNum > pages) {
            if(pages!=0){
                //把pageNum设置为最后一页,震惊
                //把pageNum设置为最后一页,震惊
                //把pageNum设置为最后一页,震惊
                pageNum = pages;
            }
            calculateStartAndEndRow();
        }
    }

问题解析

为什么返回数据???

因为我查询的页数(pageNum = 2)大于总页数(pages = 1),因此把pages赋值给pageNum,查询最后一页肯定有数据===!

为什么返回9条数据???

因为我查询的页数(pageNum = 2)大于总页数(pages = 1),因此把pages赋值给pageNum,查询最后一页根据分析就是9条===!

参考

MyBatis的分页原理pagehelper分页查询的一个坑,明明下一页没有数据了却还是返回了数据