分页查询 springboot + mybatis pagehelper

604 阅读2分钟

引言

当数据量达到一定规模,需要进行分页,避免后端内存溢出,或者前端展示体验不好。物理分页速度慢,但能避免内存溢出,逻辑分页速度快,但其原理是一次查询所有数据,根据索引分段读取,不能避免内存溢出。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 框架提供了物理分页方法,适合处理从数据库中查询后分页展示的需求。如果存在改变数据条目的处理,需要自己实现逻辑分页。