前言:
PageHelper是一款开源免费的Mybatis第三方物理分页插件。所谓物理分页,也就是每次都会根据要查询的参数去对应的数据库进行查询,真正实现了按需动态查询。逻辑分页,一般是指一次性查询所有的数据,再由service根据参数传递给前端对应页数的数据,还有一种就是传递给前端所有的数据,再由前端控制显示的数据。因此,对于数据量大的情况下,物理分页要比逻辑分页性能高很多。

分页思想
笔者在未接触过分页插件之前,曾经自己造过分页的轮子,一般来说,分页需要有这么几个参数:
符合条件的数据量count:这个一般就是根据sql条件count(1)传递回数据量;
每页的数据量pageSize:这个参数由开发者自行确定;
查询页码pageNum:这个参数一般由前端传到后端,但是要保证0<pageNum<=Math.ceil(count/pageSize)。

PageHelper的引用
关于PageHelper的引入,详情参考官网讲述:https://pagehelper.github.io/docs/howtouse/#2

PageHelper在代码中的使用
一般情况下笔者习惯使用如下代码:

  PageHelper.startPage(pageNum, pageSize);  .....省略server的内容,一般返回一个listPageInfo<类名> pageInfo = new PageInfo<>(list);

PageHelper源码解析

  1. 在PageHelper.startPage(pageNum, pageSize),这段代码表示分页的开始,意思是从第pageNum页开始,每页显示pageSize条记录。我们进入源码,发现是这么一个泛型方法:
public static <E> Page<E> startPage(int pageNum, int pageSize) {return startPage(pageNum, pageSize, DEFAULT_COUNT);}

其中,DEFAULT_COUNT我们翻阅前边,发现是一个boolean参数,

protected static boolean DEFAULT_COUNT = true;

该参数表示由分页插件默认查询符合sql条件的数据,默认为true,因此我们在使用的时候并不需要再去重复写关于count的数据库语句了。
然后源码中依次引用其他构造函数,最终使用的是如下构造器:

public static <E> Page<E> startPage(int pageNum, int pageSize, boolean count, Boolean reasonable, Boolean pageSizeZero) {Page<E> page = new Page(pageNum, pageSize, count);page.setReasonable(reasonable);page.setPageSizeZero(pageSizeZero);Page<E> oldPage = getLocalPage();//当已经执行过orderBy的时候if (oldPage != null && oldPage.isOrderByOnly()) {page.setOrderBy(oldPage.getOrderBy());}setLocalPage(page);return page;}

其中,boolean count对应前边的boolean参数DEFAULT_COUNT,reasonable参数表示分页合理化,而pageSizeZero设置为true的时候,如果pagesize设置为0(或RowBounds的limit=0),就不执行分页,返回全部结果。
从源码中可以看出,出现了getLocalPage()与setLocalPage()方法,沿着调用的方向,发现如下:

public static <T> Page<T> getLocalPage() {return (Page)LOCAL_PAGE.get();}......protected static void setLocalPage(Page page) {LOCAL_PAGE.set(page);}

这两个方法都是对用于获取Page参数,其中,又出现了 LOCAL_PAGE,其实这是一个ThreadLocal,如下:

 protected static final ThreadLocal<Page> LOCAL_PAGE = new ThreadLocal();

其中,PageHelper 方法使用了静态的 ThreadLocal 参数,分页参数和线程是绑定的。
并且,根据官方文档只有紧跟在 PageHelper.startPage 方法后的第一个 Mybatis 的查询(Select)方法会被分页。因为 PageHelper 在 finally 代码段中自动清除了 ThreadLocal 存储的对象,这是为了保证线程安全。

  1. 执行完PageHelper.startPage(pageNum, pageSize)之后,接着就是调用我们的service了,一般不用再去重复编写查询count的操作了。

  2. 最后是PageInfo<类名> pageInfo = new PageInfo<>(list); 我们进入到PageInfo的源码中,发现最后调用的是如下方法:
    public PageInfo(List list, int navigatePages) {
    super(list);
    this.isFirstPage = false;
    this.isLastPage = false;
    this.hasPreviousPage = false;
    this.hasNextPage = false;
    if (list instanceof Page) {
    Page page = (Page)list;
    this.pageNum = page.getPageNum();
    this.pageSize = page.getPageSize();
    this.pages = page.getPages();
    this.size = page.size();
    if (this.size == 0) {
    this.startRow = 0;
    this.endRow = 0;
    } else {
    this.startRow = page.getStartRow() + 1;
    this.endRow = this.startRow - 1 + this.size;
    }
    } else if (list instanceof Collection) {
    this.pageNum = 1;
    this.pageSize = list.size();
    this.pages = this.pageSize > 0 ? 1 : 0;
    this.size = list.size();
    this.startRow = 0;
    this.endRow = list.size() > 0 ? list.size() - 1 : 0;
    }

     if (list instanceof Collection) {this.navigatePages = navigatePages;this.calcNavigatepageNums();this.calcPage();this.judgePageBoudary();}
    

    }

其中navigatePages代表着导航页,默认为8,使用PageInfo这个类,你需要将查询出来的list放进去,在这个构造函数中,对于传进来的list进行一系列的instanceof判断,如果是page类型,填参即可,注意startRow和endRow的计算方法。如果是Collection类型,也是进行填参,然后执行三个方法分别是calcNavigatepageNums(); calcPage(); judgePageBoudary();
calcNavigatepageNums()源码如下:

//计算导航页面
private void calcNavigatepageNums() {int i;if (this.pages <= this.navigatePages) {this.navigatepageNums = new int[this.pages];for(i = 0; i < this.pages; ++i) {this.navigatepageNums[i] = i + 1;}} else {this.navigatepageNums = new int[this.navigatePages];i = this.pageNum - this.navigatePages / 2;int endNum = this.pageNum + this.navigatePages / 2;int i;if (i < 1) {i = 1;for(i = 0; i < this.navigatePages; ++i) {this.navigatepageNums[i] = i++;}} else if (endNum > this.pages) {endNum = this.pages;for(i = this.navigatePages - 1; i >= 0; --i) {this.navigatepageNums[i] = endNum--;}} else {for(i = 0; i < this.navigatePages; ++i) {this.navigatepageNums[i] = i++;}}}}

接着查看源码:

//计算前后页,第一页,最后一页
private void calcPage() {if (this.navigatepageNums != null && this.navigatepageNums.length > 0) {this.navigateFirstPage = this.navigatepageNums[0];this.navigateLastPage = this.navigatepageNums[this.navigatepageNums.length - 1];if (this.pageNum > 1) {this.prePage = this.pageNum - 1;}if (this.pageNum < this.pages) {this.nextPage = this.pageNum + 1;}}}

最后查看judgePageBoudary()源码:

//判断边界页面
private void judgePageBoudary() {this.isFirstPage = this.pageNum == 1;this.isLastPage = this.pageNum == this.pages || this.pages == 0;this.hasPreviousPage = this.pageNum > 1;this.hasNextPage = this.pageNum < this.pages;}

到此,pageHelper的源码解读就结束了,大体的方向就是只要计算出来总条数count,然后根据需要传进来查询的页面pageNum和每页的数据量pageSize,对count进行一个页面的计算和一些边界情况的判定与处理。体现在PageHelper中,实际上就是通过控制层传来的页数和每页显示的行数,以此为参数计算出分页对象page中的相关参数,并将list结果集保存至page中,而page继承自ArrayList,然后将page对象进行打包存放如pageInfo对象中,最后返回pageInfo对象。
最后过一下PageInfo的参数:

//当前页
private int pageNum;
//每页的数量
private int pageSize;
//当前页的数量
private int size;
//当前页面第一个元素在数据库中的行号
private int startRow;
//当前页面最后一个元素在数据库中的行号
private int endRow;//总页数
private int pages;//前一页
private int prePage;//后一页
private int nextPage;
//是否为第一页
private boolean isFirstPage = false;
//是否为最后一页
private boolean isLastPage = false;
//是否有前一页
private boolean hasPreviousPage = false;
//是否有下一页
private boolean hasNextPage = false;
//导航页码数
private int navigatePages;
//所有导航页号
private int[] navigatepageNums;
//导航页的第一页
private int navigateFirstPage;
//导航页的最后一页
private int navigateLastPage;

浅谈分页插件PageHelper相关推荐

  1. 解决使用mybatis分页插件PageHelper的一个报错问题

    解决使用mybatis分页插件PageHelper的一个报错问题 参考文章: (1)解决使用mybatis分页插件PageHelper的一个报错问题 (2)https://www.cnblogs.co ...

  2. MyBatis分页插件PageHelper使用练习

    转载自:http://git.oschina.net/free/Mybatis_PageHelper/blob/master/wikis/HowToUse.markdown 1.环境准备: 分页插件p ...

  3. vue分页+spring boot +分页插件pagehelper

    vue分页+spring boot +分页插件pagehelper https://blog.csdn.net/baidu_38603246/article/details/98854013

  4. 【MyBatis】MyBatis分页插件PageHelper的使用

    转载自 https://www.cnblogs.com/shanheyongmu/p/5864047.html 好多天没写博客了,因为最近在实习,大部分时间在熟悉实习相关的东西,也没有怎么学习新的东西 ...

  5. MyBatis学习总结(17)——Mybatis分页插件PageHelper

    2019独角兽企业重金招聘Python工程师标准>>> 如果你也在用Mybatis,建议尝试该分页插件,这一定是最方便使用的分页插件. 分页插件支持任何复杂的单表.多表分页,部分特殊 ...

  6. SpringBoot集成MyBatis的分页插件PageHelper

    [写在前面] 项目的后台管理系统需要展示所有资源信息,select * 虽然方便但数据量过于庞大会严重降低查找效率,页面加载慢,用户体验差.分页自然是必要选择,但原生的方法过于繁杂.MyBatis的分 ...

  7. hql分页获取数据总数_最简洁的分页插件PageHelper

    最简洁的分页插件PageHelper 简介 大部分后台管理项目,用的最多的展现方式是列表显示,所以我把列表查询中的分页插件拿出来写一下,方便以后写代码的时候直接复制粘贴.在我们最熟悉的springbo ...

  8. java分页插件PageHelper的内置list数据操作失败

    问题描述: java分页插件PageHelper的内置list数据在hanlder处理器中获取失败,但是测试确通过了,如下图 但是测试代码成功 @ContextConfiguration(locati ...

  9. Spring Boot系列教程八: Mybatis使用分页插件PageHelper

    一.前言 上篇博客中介绍了spring boot集成mybatis的方法,基于上篇文章这里主要介绍如何使用分页插件PageHelper.在MyBatis中提供了拦截器接口,我们可以使用PageHelp ...

最新文章

  1. python DbUtils 使用教程
  2. 台式电脑一般价钱多少_让你少走弯路,看我怎么花低价钱配出高配置的台式电脑的...
  3. 手持机设备公司(WINCE/ANDROID/LINUX)
  4. ACK容器服务发布virtual node addon,快速部署虚拟节点提升集群弹性能力
  5. Laravel框架与ThinkPHP一些不同点
  6. awk输出最后一列的命令
  7. java中treemap释放_在Java中从TreeMap删除键
  8. 从60%的BI和数据仓库项目失败,看出从业者那些不堪的乱象
  9. Android开发UI之GridLayout的使用
  10. python wmi读取网卡MAC地址、CPU序列号、硬盘序列号、主板序列号、BIOS序列号
  11. 华为机试HJ77:火车进站
  12. 北京工商大学c语言复试试题,2016年北京工商大学计算机与信息工程学院C语言程序设计复试笔试仿真模拟题...
  13. WPF 文字换行TextWrapping 显示不全用省略号TextTrimming 显示不全弹提示内容ToolTip
  14. 数据挖掘概念与技术第三版 范明、孟晓峰译 第三章习题答案
  15. Python中遇到pcap not match 问题
  16. Pytorch练习--绘制Loss曲线
  17. 学历,工作经验,火焰纹章
  18. ceph osd为down的情况
  19. 光纤收发器结构介绍和故障解决
  20. Jenkins+Bitbucket webhook 自动触发构建

热门文章

  1. iOS模拟器调试WebView
  2. python为什么那么好看_为什么Python代码要写得美观而明确
  3. 一个普通应届生,简历上写什么能过BAJT的海选初筛?(附实例模板)
  4. 经纬度坐标与实际距离的计算转换
  5. 【gt+】RS485详解
  6. EasyMesh和802.11s对比
  7. 一些关于船舶撞桥的数据
  8. 软件下载安装卸载及文件夹管理
  9. 安装web服务器组件,在Windows2003系统中如何安装Web服务器组件?
  10. 精品微信小程序中医药配方小程序药方+后台系统|前后分离VUE