引言
当数据量达到一定规模,需要进行分页,避免后端内存溢出,或者前端展示体验不好。物理分页速度慢,但能避免内存溢出,逻辑分页速度快,但其原理是一次查询所有数据,根据索引分段读取,不能避免内存溢出。pagehelper 是 mybatis 框架的分页插件,直接实现对来自数据库查询条目的分页封装,常用于前端查询接口。
依赖引入
pagehelper-spring-boot-starter 集成分页插件到 springboot,2022年6月18日更新 v1.4.3 版本,支持 v2.7.0 版本 springboot,v3.5.10 版本 mybatis,v5.3.1 版本 pagehelper。
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.4.3</version>
</dependency>
项目启动文件 application.yaml 中添加配置:
pagehelper:
helper-dialect: mysql
reasonable: true
support-methods-arguments: true
params: count=countSql
其中,reasonable 的设置用于处理分页上下限,如果输入页码为负数或为零,返回第一页,如果输入页码大于上限,返回最后一页。
直接从数据库中分页查询
相当于在 sql 查询前,通过分页拦截器加入 limit start_location_index,select_num 的限制条件,属于物理分页。
public PageResult<dataVO> listByPage(String params, Integer page, Integer pageSize) {
PageInfo<dataDO> dataDOListPage = PageHelper.startPage(page, pageSize)
.doSelectPageInfo(() -> dataDOMapper.listByConditions(params));
return PageResult.<dataVO>builder()
.dataList(dataDOListPage.getList().stream().map(this::toVO).collect(Collectors.toList()))
.page(dataDOListPage.getPageNum())
.pageSize(dataDOListPage.getPageSize())
.total(dataDOListPage.getTotal())
.build();
}
存在改变数据条目的处理
如果从数据库查询的条目不直接用于分页展示,还需要通过一些改变数据条目的处理,比如二次查询、过滤,就不适合使用物理分页,因为 pageHelper.startPage 只对首个查询生效。这时需要自己实现一个逻辑分页,通过 subList 对处理结果进行分页。
@Data
public class PagingList<T> {
/**
* 总页数,默认为1
*/
private int totalPage = 1;
/**
* 当前是第几页,默认为1
*/
private int curPageNo = 1;
/**
* 每页的大小,默认为10
*/
private int pageSize = 10;
private List<T> pagelist = null;
public PagingList(List<T> datalist, int page, int pageSize) {
this.curPageNo = page;
this.pageSize = pageSize;
calTotalPage(datalist, pageSize);
generatePageList(datalist);
}
private void calTotalPage(List<T> datalist, int pageSize) {
if (pageSize <= 0) {
throw new IllegalArgumentException("Paging size must be greater than zero.");
}
if (null == datalist) {
throw new NullPointerException("Paging resource list must be not null.");
}
if (datalist.size() % pageSize > 0) {
this.totalPage = (datalist.size() / pageSize) + 1;
} else {
this.totalPage = datalist.size() / pageSize;
}
}
private void generatePageList(List<T> datalist) {
this.pagelist = this.totalPage - this.curPageNo >= 1 ?
datalist.subList((this.curPageNo - 1) * this.pageSize, this.curPageNo * this.pageSize) :
datalist.subList((this.curPageNo - 1) * this.pageSize, datalist.size());
}
}
然后不用借助 pageHelper.startPage 和 PageInfo,直接将自己的分页结果装入 PageResult,注意需要自己统计 total 并存入。
PagingList<dataVO> paging = new PagingList<>(dataVOList, page, pageSize);
PageResult<dataVO> pageResult = new PageResult<>();
pageResult.setDataList(paging.getPagelist());
pageResult.setPage(paging.getCurPageNo());
pageResult.setPageSize(paging.getPageSize());
pageResult.setTotal((long)dataVOList.size());
总结
综上,springboot + mybatis pagehelper 框架提供了物理分页方法,适合处理从数据库中查询后分页展示的需求。如果存在改变数据条目的处理,需要自己实现逻辑分页。