1. 问题现象
在使用 pagehelper 插件开发查询接口时,出现分页失效的情况:接口返回了全部数据而非当前页数据,且 pageinfo 对象中的分页元数据(如 total 总条数、pages 总页数)计算错误。
错误代码示例
public baseresponse<mydatadto> querydatalist(int pagenum, int pagesize, map<string, object> params) {
baseresponse response = new baseresponse();
// 1. 错误:在设置分页参数前执行了数据库查询
list<mydatadto> datalist = mymapper.selectdata(params);
// 2. 判空逻辑
if (!collectionutils.isempty(datalist)) {
// 3. 错误:查询早已完成,此时调用 startpage 无效
pagehelper.startpage(pagenum, pagesize);
// 4. 错误:直接使用全量 list 包装 pageinfo,无法获取数据库真实总数
pageinfo<mydatadto> pageinfo = new pageinfo<>(datalist);
response.setresult(wrappageresult(pageinfo));
return response;
}
response.setresult(collections.empty_list);
return response;
}
2. 原理分析
pagehelper 的核心机制基于 threadlocal 和 mybatis 拦截器(interceptor)。
2.1 执行流程
pagehelper 不是在内存中对结果集进行截取,而是通过拦截器修改 sql 语句。
设置参数:调用 pagehelper.startpage(...) 时,插件会将分页参数(pagenum, pagesize)存入当前线程的 threadlocal 中。
拦截 sql:当 mybatis 执行 mapper 方法时,pagehelper 拦截器会触发。
sql 改写:
- 拦截器检查
threadlocal中是否存在分页参数。 - 若存在:拦截器会根据数据库方言(如 mysql)生成
select count(0)语句获取总数,并将原 sql 改写为带limit/offset的分页 sql 执行。 - 若不存在:拦截器直接放行,执行原始 sql。
清理上下文:sql 执行结束后,拦截器会清除 threadlocal 中的分页参数,避免污染后续查询。
2.2 失效原因
在上述错误代码中:
- 执行顺序错误:
mymapper.selectdata在pagehelper.startpage之前执行。 - 拦截失败:执行查询时,
threadlocal中没有任何分页参数,拦截器未生效,mybatis 执行了全量查询。 - 参数无效:查询结束后才调用
startpage,虽然设置了threadlocal,但 sql 交互已结束,该参数未被消费。 - 元数据错误:
pageinfo接收的是全量 list,因此它只能基于 list 的大小计算total,导致分页信息不符合预期。
3. 正确实现
核心原则:pagehelper.startpage 必须紧邻 mapper 查询方法之前调用。
修正代码
public baseresponse<mydatadto> querydatalist(int pagenum, int pagesize, map<string, object> params) {
baseresponse response = new baseresponse();
// 1. 设置分页参数(存入 threadlocal)
pagehelper.startpage(pagenum, pagesize);
// 2. 执行查询(拦截器生效,自动改写 sql 并执行 count 查询)
// 注意:此时返回的 list 实际类型为 page<e>
list<mydatadto> datalist = mymapper.selectdata(params);
// 3. 获取分页结果
pageinfo<mydatadto> pageinfo = new pageinfo<>(datalist);
// 4. 结果处理(pageinfo 可安全处理空集合)
if (!collectionutils.isempty(datalist)) {
// pageinfo.gettotal() 为数据库真实总数
response.setresult(wrappageresult(pageinfo));
} else {
response.setresult(collections.empty_list);
}
return response;
}
4. 最佳实践与注意事项
严格遵守调用顺序 必须保证 startpage -> mapper查询 -> pageinfo包装 的执行顺序。
避免逻辑穿插 严禁在 startpage 和 mapper查询 之间插入其他 sql 操作或复杂逻辑。
风险:pagehelper 的分页参数是“一次性消费”的。如果在分页查询前插入了其他 sql(如查询用户信息),分页参数会被那条 sql 消费掉,导致原本需要分页的主查询失效。
pageinfo 的健壮性 无需为了判空调整代码顺序。pageinfo 对空 list 有良好的兼容性,若查询结果为空,它会自动设置 total=0,不会抛出异常。
大数据量风险 如果因顺序错误导致分页失效,全量查询可能会将百万级数据加载至内存,极易引发 oom(内存溢出),影响系统稳定性。
到此这篇关于pagehelper中分页失效的原因分析与正确方法实践的文章就介绍到这了,更多相关pagehelper分页失效解决内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论