1. PageHelper简介

一款很好用的 分页插件,支持多种数据库,拿来即用

2.环境

springboot 2.7.1、 jdk11、pagehelper1.4.2

3. PageHelper 使用

3.1 导包

<!-- 版本需要1.4 以及以上 否则会报错 -->
<dependency><groupId>com.github.pagehelper</groupId><artifactId>pagehelper-spring-boot-starter</artifactId><version>1.4.1</version>
</dependency>

启动报错: 使用1.4 以下版本,会报如下错

解决办法:二选一即可,推荐第一种

  1. 升高 pageHelper版本到1.4.1 及以上
<dependency><groupId>com.github.pagehelper</groupId><artifactId>pagehelper-spring-boot-starter</artifactId><version>1.4.1</version>
</dependency>
  1. 如下配置
 spring:main:allow-circular-references: true

3.2 简单使用

 // pageNum 当前页数, pageSize 每页条数;/* 注意:  1. 只有紧跟在PageHelper.startPage方法后的第一个Mybatis的查询(Select)方法会被分页。2. 由于嵌套结果方式会导致结果集被折叠,因此分页查询的结果在折叠后总数会减少,所以无法保证分页结果数量正确。*/PageHelper.startPage(pageNum, pageSize);List<SysUser1> list1 = sysUser1Mapper.example();

3.3 其它使用方式

    // 方式1PageHelper.startPage(1, 1);List<SysUser1> list1 = sysUser1Mapper.example();System.out.println("方式1: " + list1);System.out.println();// 方式二PageHelper.offsetPage(1, 1);List<SysUser1> list2 = sysUser1Mapper.example();System.out.println("方式2: " + list2);System.out.println();// 方式三List<SysUser1> list3 = session.selectList("com.example.demoproject.server.dao.SysUser1Mapper.list", null, new PageRowBounds(1, 1));System.out.println("方式3: " + list3);System.out.println();/*方式四,参数方法调用1. 接口方法定义, xml 中不需要处理这两个参数  List<SysUser1> selectByPageNumSize(@Param("pageNum") int pageNum, @Param("pageSize") int pageSize);2. supportMethodsArguments=true*/List<SysUser1> list4 = sysUser1Mapper.selectByPageNumSize(1, 1);System.out.println("方式4: " + list4);System.out.println();/*第五种1. 参数对象 com.example.demoproject.server.common.req.BaseReq 属性有 pageNum 和 pageSize 只要参数有值,也会被分页2. supportMethodsArguments=true*/BaseReq baseReq = new BaseReq();baseReq.setPageNum(1);baseReq.setPageSize(1);List<SysUser1> list5 = sysUser1Mapper.selectByPageNumSize(baseReq);System.out.println("方式5: " + list5);System.out.println();/*第六种,ISelect 接口方式; 也可以 lambda表达式方式6.1 doSelectPage6.2 doSelectPageInfo6.3 PageHelper.count*/// 6.1 doSelectPagePage<SysUser1> page = PageHelper.startPage(1, 1).doSelectPage(new ISelect() {@Overridepublic void doSelect() {List<SysUser1> list6 = sysUser1Mapper.example();System.out.println("方式6-doSelectPage-1: " + list6);System.out.println();}});System.out.println("方式6-doSelectPage-2: " + page);System.out.println();// 6.2 doSelectPageInfoPageInfo<SysUser1> objectPageInfo = PageHelper.startPage(1, 1).doSelectPageInfo(new ISelect() {@Overridepublic void doSelect() {List<SysUser1> list6 = sysUser1Mapper.example();System.out.println("方式6-doSelectPageInfo-1: " + list6);System.out.println();}});System.out.println("方式6-doSelectPageInfo-2: " + objectPageInfo);System.out.println();// 6.3 PageHelper.countlong total = PageHelper.count(new ISelect() {@Overridepublic void doSelect() {List<SysUser1> list6 = sysUser1Mapper.example();System.out.println("方式6-count-1: " + list6);System.out.println();}});System.out.println("方式6-total-2: " + total);System.out.println();

3.4 配置项(一般配置在 yml后缀文件中)

#格式:
# pagehelper:
#   xxxxx: xx
# 如:
# pagehelper:
#   offsetAsPageNum: false # ############################常用配置####################################################
# helperDialect  数据库方言, 默认:分页插件会自动检测当前的数据库链接,自动选择合适的分页方式
# 可选择 oracle,mysql,postgresql,db2,sqlserver,sqlserver2012 等
# 特别注意: 使用 SqlServer2012 数据库时, 需要手动指定为 sqlserver2012, 否则会使用 SqlServer2005 的方式进行分页# offsetAsPageNum:默认值为 false, 该参数对使用 RowBounds 作为分页参数时有效。 当该参数设置为 true 时,会将 RowBounds 中的 offset 参数当成 pageNum 使用,可以用页码和页面大小两个参数进行分页。# rowBoundsWithCount:默认值为false,该参数对使用 RowBounds 作为分页参数时有效。 当该参数设置为true时,使用 RowBounds 分页会进行 count 查询。# pageSizeZero:默认值为 false,当该参数设置为 true 时,如果 pageSize=0 或者 RowBounds.limit = 0 就会查询出全部的结果(相当于没有执行分页查询,但是返回结果仍然是 Page 类型)。# reasonable:分页合理化参数,默认值为false。当该参数设置为 true 时,pageNum<=0 时会查询第一页, pageNum>pages(超过总数时),会查询最后一页。默认false 时,直接根据参数进行查询。# params:为了支持startPage(Object params)方法,增加了该参数来配置参数映射,用于从对象中根据属性名取值, 可以配置 pageNum,pageSize,count,pageSizeZero,reasonable,不配置映射的用默认值, 默认值为pageNum=pageNum;pageSize=pageSize;count=countSql;reasonable=reasonable;pageSizeZero=pageSizeZero。# supportMethodsArguments:支持通过 Mapper 接口参数来传递分页参数,默认值false,分页插件会从查询方法的参数值中,自动根据上面 params 配置的字段中取值,查找到合适的值时就会自动分页# ############################不常用配置####################################################
# autoRuntimeDialect:默认值为 false。设置为 true 时,允许在运行时根据多数据源自动识别对应方言的分页 (不支持自动选择sqlserver2012,只能使用sqlserver)# closeConn:默认值为 true。当使用运行时动态数据源或没有设置 helperDialect 属性自动获取数据库类型时,会自动获取一个数据库连接, 通过该属性来设置是否关闭获取的这个连接,默认true关闭,设置为 false 后,不会关闭获取的连接,这个参数的设置要根据自己选择的数据源来决定。

配置项说明: springboot 会自动联想,此处注意如果联想出来不是驼峰命名,也不报错,但是参数会不生效
如:

4. 原理

4.1 问题

  上述说的简单使用方式,只在要执行的sql之前加一行代码就行,那这行代码是怎么做到的呢?想搞清楚这个首先引出最简单的问题是分页参数怎么和sql执行关联,sql又是什么时候执行的?

4.2 分页参数设置原理

  我们可以跟踪进去看下发现代码是这样的,分两步,第一步是,分页参数包装为Page对象,第二步是,Page对象放入 ThreadLocal 中。

  ThreadLocal 是什么呢,简单说是 本地线程变量,存储了当前线程所需要的数据副本,只有当前线程可以使用,作为解决并发问题的一种实现(spring就是基于此)。也就是说,如果不手动清理的话,你放进去的数据,在线程销毁之前都能用,所以一般用这个的时候不用的数据要及时手动清理防止内存泄漏。

    public static <E> Page<E> startPage(int pageNum, int pageSize, boolean count, Boolean reasonable, Boolean pageSizeZero) {// 1.构建page对象,记住这个对象Page<E> page = new Page<E>(pageNum, pageSize, count);page.setReasonable(reasonable);page.setPageSizeZero(pageSizeZero);//当已经执行过orderBy的时候Page<E> oldPage = getLocalPage();if (oldPage != null && oldPage.isOrderByOnly()) {page.setOrderBy(oldPage.getOrderBy());}// 2.page对象 放入 ThreadLocal 中setLocalPage(page);return page;}

4.3 分页实现

4.3.1 程序入口

  PageInterceptor.intercept 方法。首先看PageInterceptor这个类,拦截了两个方法,一个是四个参数,一个是六个参数的 实现类 org.apache.ibatis.plugin.Interceptor(mybatis提供的插件类)

  再看方法 intercept ,重写了mybatis提供的方法,核心统计代码如下,主要就是 skip方法 有三个实现类 根据不同配置有不同的实现,此处是PageHelper

 //调用方法判断是否需要进行分页,如果不需要,直接返回结果if (!dialect.skip(ms, parameter, rowBounds)) {//判断是否需要进行 count 查询(有了分页参数才会进来,此处不知道为什么还需要再次判断下)if (dialect.beforeCount(ms, parameter, rowBounds)) {//查询总数 此处就是查询核心代码Long count = count(executor, ms, parameter, rowBounds, null, boundSql);//处理查询总数,返回 true 时继续分页查询,false 时直接返回if (!dialect.afterCount(count, parameter, rowBounds)) {//当查询总数为 0 时,直接返回空的结果return dialect.afterPage(new ArrayList(), parameter, rowBounds);}}resultList = ExecutorUtil.pageQuery(dialect, executor,ms, parameter, rowBounds, resultHandler, boundSql, cacheKey);} else {//rowBounds用参数值,不使用分页插件处理时,仍然支持默认的内存分页resultList = executor.query(ms, parameter, rowBounds, resultHandler, cacheKey, boundSql);}

4.3.2 分页核心方法

  PageHelper.skip() 主要是 pageParams.getPage 获取分页信息

 @Overridepublic boolean skip(MappedStatement ms, Object parameterObject, RowBounds rowBounds) {if (ms.getId().endsWith(MSUtils.COUNT)) {throw new RuntimeException("在系统中发现了多个分页插件,请检查系统配置!");}// 获取分页信息Page page = pageParams.getPage(parameterObject, rowBounds);if (page == null) {return true;} else {//设置默认的 count 列if (StringUtil.isEmpty(page.getCountColumn())) {page.setCountColumn(pageParams.getCountColumn());}autoDialect.initDelegateDialect(ms, page.getDialectClass());return false;}}

  PageParams.getPage(Object parameterObject, RowBounds rowBounds)重头戏来了,分页处理,处理完回到 skip 方法

public Page getPage(Object parameterObject, RowBounds rowBounds) {/*分页方式: PageHelper.startPage 、 RowBound 方式 、 supportMethodsArguments=true*/Page page = PageHelper.getLocalPage();if (page == null) {// 2.没有主动设置分页 此处支持自己设置 分页参数时的逻辑此处用的是等等不是 equalsif (rowBounds != RowBounds.DEFAULT) {// 3. rowBounds分页逻辑if (offsetAsPageNum) {page = new Page(rowBounds.getOffset(), rowBounds.getLimit(), rowBoundsWithCount);} else {page = new Page(new int[]{rowBounds.getOffset(), rowBounds.getLimit()}, rowBoundsWithCount);//offsetAsPageNum=false的时候,由于PageNum问题,不能使用reasonable,这里会强制为falsepage.setReasonable(false);}if(rowBounds instanceof PageRowBounds){PageRowBounds pageRowBounds = (PageRowBounds)rowBounds;page.setCount(pageRowBounds.getCount() == null || pageRowBounds.getCount());}} else if(parameterObject instanceof IPage || supportMethodsArguments){// IPage 或 supportMethodsArguments 参数配置处理(入参里面拿分页参数封装为page对象)try {page = PageObjectUtil.getPageFromObject(parameterObject, false);} catch (Exception e) {return null;}}if(page == null){return null;}// 没有显示设置分页参数 且需要分页时 分页参数放入 ThreadLocalPageHelper.setLocalPage(page);}//分页合理化if (page.getReasonable() == null) {page.setReasonable(reasonable);}//当设置为true的时候,如果pagesize设置为0(或RowBounds的limit=0),就不执行分页,返回全部结果if (page.getPageSizeZero() == null) {page.setPageSizeZero(pageSizeZero);}return page;}

4.3.3 分页

   分页参数解析后,如果需要分页,则skip 校验通过,进入if逻辑 执行分页sql,然后在查询数据,最终将数据封装为 Page(实际是list)返回,详见 4.3.1 程序入口 注释说明

4.3.4 总结

  1. PageHelper是一款国人自己编写的分页插件,结合mybatis、 spring 使用
  2. 简单使用方式,只需要在需要执行的sql上面,加入代码 PageHelper.startPage(pageNum, pageSize); 即可实现
  3. 分页原理是 通过ThreadLocal 或者 开启自动查找参数中的分页参数,在执行真实sql 之前查询总数,然后返回Page(list)对象,实现分页效果

4.4 资源下载

文中所属项目下载地址: https://download.csdn.net/download/m0_46861007/86248872

PageHelper分页插件使用 及原理相关推荐

  1. PageHelper分页插件的原理是什么

    PageHelper是一款好用的开源免费的Mybatis第三方物理分页插件,其实我并不想加上好用两个字,但是为了表扬插件作者开源免费的崇高精神,我毫不犹豫的加上了好用一词作为赞美. 原本以为分页插件, ...

  2. concurrenthashmap实现原理_Mybatis:PageHelper分页插件源码及原理剖析

    PageHelper是一款好用的开源免费的Mybatis第三方物理分页插件,其实我并不想加上好用两个字,但是为了表扬插件作者开源免费的崇高精神,我毫不犹豫的加上了好用一词作为赞美. 原本以为分页插件, ...

  3. PageHelper分页插件源码及原理剖析

    摘要: com.github.pagehelper.PageHelper是一款好用的开源免费的Mybatis第三方物理分页插件. PageHelper是一款好用的开源免费的Mybatis第三方物理分页 ...

  4. oracle 分页_Mybatis:PageHelper分页插件源码及原理剖析

    点击上方"Java知音",选择"置顶公众号" 技术文章第一时间送达! 作者:祖大俊 my.oschina.net/zudajun/blog/745232 Pag ...

  5. mybatis 分页需要的jar包下载_牛逼哄哄的PageHelper分页插件到底牛在哪里?

    你知道的越多,不知道的就越多,业余的像一棵小草! 你来,我们一起精进!你不来,我和你的竞争对手一起精进! 编辑:业余草 urlify.cn/z2IFn2 推荐:https://www.xttblog. ...

  6. springboot2.0.5集成mybatis(PageHelper分页插件、generator插件使用)

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/zab635590867/article ...

  7. PageHelper 分页插件使用总结

    一个简洁的博客网站:http://lss-coding.top,欢迎大家来访 学习娱乐导航页:http://miss123.top/ 一.认识 PageHelper **官网:**https://pa ...

  8. spring boot+mybatis+thymeleaf+pagehelper分页插件实现分页功能

    文章目录 前言 正文 业务场景 后端 pom.xml application.yml 实体类video.java和User.java----映射VideoMapper.xml----VideoMapp ...

  9. spring boot整合mybatis+通用mapper+pagehelper分页插件

    spring boot整合mybatis+通用mapper+pagehelper分页插件 pom依赖 <?xml version="1.0" encoding="U ...

最新文章

  1. log4j(七)——log4j.xml简单配置样例说明
  2. 实验五 数据库完整性技术
  3. C、C++和MFC中文件的操作
  4. 泛型那点儿事儿 泛型概述 简单样例代码
  5. Unity多人游戏集合
  6. 功放与喇叭的匹配原则
  7. 班章管家:理财产品哪个好一些?从以下几个方面比较
  8. 业务需求调研经验分享
  9. CSRF漏洞利用以及防御手段(详细解释)
  10. 莫比乌斯函数莫比乌斯反演
  11. 共享书籍同类产品调研
  12. 企业微信加载html模板,企业微信公众号页面模板使用的方法是什么?
  13. 零基础请进,影视解说必备的5个工具|不可错过的免费配音软件
  14. 本周AI热点回顾:GAN生成的超级马里奥关卡,可以永不通关的那种;MLP回归,纯多层感知机视觉架构媲美CNN、ViT...
  15. 云宏大讲坛 | SDN、NV在云数据中心的应用场景
  16. LeetCode日记(香槟塔)第三天
  17. [论文阅读笔记53]2021深度神经方法的关系三元组抽取综述
  18. c语言循环 等待,[转载]while语句实现等待一段时间:编写延时循环
  19. 企业上云服务商之华山论剑
  20. 【实战】——以波士顿房价为例进行数据的相关分析和回归分析

热门文章

  1. ctp java_上期CTP 封装JAVA API window Swig
  2. C#中的Panel滚动条控制
  3. Web前端入门(五)HTML常用特殊字符
  4. mybatis之大于、小于、大于等于和小于等于的写法
  5. java实现日期月份的间隔数
  6. 淘宝开店-新手卖家上哪找货源
  7. Win32 GetKeyState和getasynckeystate
  8. 模拟退火解决旅行商问题
  9. 【HIVE】Hive 通过控制角色(role)权限访问
  10. 蓝牙协议之GATT学习笔记