浅谈分页插件PageHelper
前言:
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源码解析:
- 在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 存储的对象,这是为了保证线程安全。
执行完PageHelper.startPage(pageNum, pageSize)之后,接着就是调用我们的service了,一般不用再去重复编写查询count的操作了。
最后是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相关推荐
- 解决使用mybatis分页插件PageHelper的一个报错问题
解决使用mybatis分页插件PageHelper的一个报错问题 参考文章: (1)解决使用mybatis分页插件PageHelper的一个报错问题 (2)https://www.cnblogs.co ...
- MyBatis分页插件PageHelper使用练习
转载自:http://git.oschina.net/free/Mybatis_PageHelper/blob/master/wikis/HowToUse.markdown 1.环境准备: 分页插件p ...
- vue分页+spring boot +分页插件pagehelper
vue分页+spring boot +分页插件pagehelper https://blog.csdn.net/baidu_38603246/article/details/98854013
- 【MyBatis】MyBatis分页插件PageHelper的使用
转载自 https://www.cnblogs.com/shanheyongmu/p/5864047.html 好多天没写博客了,因为最近在实习,大部分时间在熟悉实习相关的东西,也没有怎么学习新的东西 ...
- MyBatis学习总结(17)——Mybatis分页插件PageHelper
2019独角兽企业重金招聘Python工程师标准>>> 如果你也在用Mybatis,建议尝试该分页插件,这一定是最方便使用的分页插件. 分页插件支持任何复杂的单表.多表分页,部分特殊 ...
- SpringBoot集成MyBatis的分页插件PageHelper
[写在前面] 项目的后台管理系统需要展示所有资源信息,select * 虽然方便但数据量过于庞大会严重降低查找效率,页面加载慢,用户体验差.分页自然是必要选择,但原生的方法过于繁杂.MyBatis的分 ...
- hql分页获取数据总数_最简洁的分页插件PageHelper
最简洁的分页插件PageHelper 简介 大部分后台管理项目,用的最多的展现方式是列表显示,所以我把列表查询中的分页插件拿出来写一下,方便以后写代码的时候直接复制粘贴.在我们最熟悉的springbo ...
- java分页插件PageHelper的内置list数据操作失败
问题描述: java分页插件PageHelper的内置list数据在hanlder处理器中获取失败,但是测试确通过了,如下图 但是测试代码成功 @ContextConfiguration(locati ...
- Spring Boot系列教程八: Mybatis使用分页插件PageHelper
一.前言 上篇博客中介绍了spring boot集成mybatis的方法,基于上篇文章这里主要介绍如何使用分页插件PageHelper.在MyBatis中提供了拦截器接口,我们可以使用PageHelp ...
最新文章
- python DbUtils 使用教程
- 台式电脑一般价钱多少_让你少走弯路,看我怎么花低价钱配出高配置的台式电脑的...
- 手持机设备公司(WINCE/ANDROID/LINUX)
- ACK容器服务发布virtual node addon,快速部署虚拟节点提升集群弹性能力
- Laravel框架与ThinkPHP一些不同点
- awk输出最后一列的命令
- java中treemap释放_在Java中从TreeMap删除键
- 从60%的BI和数据仓库项目失败,看出从业者那些不堪的乱象
- Android开发UI之GridLayout的使用
- python wmi读取网卡MAC地址、CPU序列号、硬盘序列号、主板序列号、BIOS序列号
- 华为机试HJ77:火车进站
- 北京工商大学c语言复试试题,2016年北京工商大学计算机与信息工程学院C语言程序设计复试笔试仿真模拟题...
- WPF 文字换行TextWrapping 显示不全用省略号TextTrimming 显示不全弹提示内容ToolTip
- 数据挖掘概念与技术第三版 范明、孟晓峰译 第三章习题答案
- Python中遇到pcap not match 问题
- Pytorch练习--绘制Loss曲线
- 学历,工作经验,火焰纹章
- ceph osd为down的情况
- 光纤收发器结构介绍和故障解决
- Jenkins+Bitbucket webhook 自动触发构建