场景复现
数据库初始化有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条===!