PageHelper 分页查询【底层代码-图文分析】【原理篇】

🍰 个人主页:不摆烂的小劉
🍞文章有不合理的地方请各位大佬指正。
🍉文章不定期持续更新,如果我的文章对你有帮助➡️ 关注🙏🏻 点赞👍 收藏⭐️

前段时间,出了一个线上Bug——Pagehelper分页查询失效。仔细研究了一下Pagehelper。
在这里插入图片描述

直接上代码,下面是常见的分页应用代码。

PageMethod.startPage(dto.getPageNum(), dto.getPageSize());
List<ProductVO> products = productDAO.listProducts(dto);
return new PageInfo<>(products);

上面三行代码做了什么事?怎么做的?

一、前置知识

1.Sql中的limit

limit 10,20 第11 行(因为索引从 0 开始,偏移量 10 就对应第 11 行)开始选取 20 行数据

limit 10 选取 前10 行数据

二、分页原理

1.PageMethod.startPage(dto.getPageNum(), dto.getPageSize());
  • 设置当前要查询的页码(dto.getPageNum())和每页显示的记录数量(dto.getPageSize()

在这里插入图片描述

  • 将分页参数存放到ThreadLocal中:
  • ThreadLocal在线程内直接获取资源,避免方法调用链中频繁地传递分页参数

在这里插入图片描述

2.List<ProductVO> products = productDAO.listProducts(dto);
2.1MyBatis拦截器
  • 执行数据库查询,自动应用分页逻辑
  • Pagehelper底层通过拦截器实现分页.参考MyBatis 的拦截器的文档部分,Executor 中的 query 方法可以被拦截,被拦截有两个方法
<E> List<E> query(
      MappedStatement ms,
      Object parameter,
      RowBounds rowBounds,
      ResultHandler resultHandler,
      CacheKey cacheKey,
      BoundSql boundSql) throws SQLException;

<E> List<E> query(
      MappedStatement ms,
      Object parameter,
      RowBounds rowBounds,
      ResultHandler resultHandler) throws SQLException;

6参数query查询, 4参数query 查询

Mybatis通过org.apache.ibatis.session.Configuration 加入拦截,MyBatis 会按照拦截器配置的顺序依次添加到 interceptorChain

public void addInterceptor(Interceptor interceptor) {
    interceptorChain.addInterceptor(interceptor);
}
2.2PageHelper拦截器PageInterceptor

在这里插入图片描述

  • 注解 @Intercepts:定义了一个拦截器,用于拦截MyBatisExecutor类中的query方法。
  • 注解 @Signature:指定了要拦截的方法签名,包括方法类型、方法名和参数类型。
  • 可以看出这里拦截了Executor 中6参数query查询, 4参数query 查询
  • PageInterceptor中的intercept方法是整个分页查询的关键,核心代码如下:
    在这里插入图片描述
    流程图
    在这里插入图片描述
    围绕这块核心代码分析一下:
  1. 判断是否需要进行 count 查询dialect.beforeCount(ms, parameter, rowBounds)
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述在这里插入图片描述
    在设置分页参数时,两种构造器,设置count是否开启查询总数
//默认开启 this.count = true
 PageMethod.startPage(dto.getPageNum(), dto.getPageSize());
 // 关闭查询总数
 PageMethod.startPage(dto.getPageNum(), dto.getPageSize(),false);
  1. 查询总数Long count = count(executor, ms, parameter, rowBounds, null, boundSql);
    执行自动创建的 count 查询
    在这里插入图片描述
    在这里插入图片描述
    获取查询总数的Sql
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    将查询的Sql包起来,拼装count(Sql) ,查询总数。

  2. 执行查询resultList = ExecutorUtil.pageQuery(dialect, executor, ms, parameter, rowBounds, resultHandler, boundSql, cacheKey);
    在这里插入图片描述

  • Sql如何拼接limit?,?
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    获取limit,拼接到查询Sql尾部
    在这里插入图片描述
  1. 分页查询后,处理分页结果集return dialect.afterPage(resultList, parameter, rowBounds);
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    将查询到的数据集合集存放到Page集合中
    在这里插入图片描述
    Page属于List集合

Page类定义
public class Page extends ArrayList implements Closeable
在这里插入图片描述

3.new PageInfo<>(products);

先在PageInfo父类中执行supep(this)计算数据集的总数,再设置分页参数
在这里插入图片描述
PageInfo父类中计算总数和设置分页参数都会判断是否属于Page易错

以上是在底层代码层面解析分页插件Pagehelper的原理.

三、思考

1.为什么将查询数据存放到Page中?

查询结果(即数据列表),还封装了与分页相关的其他信息,比如当前页码、总记录数、每页显示的记录数等

2.是否开启总数查询?
  • 未开启查询总数
    PageMethod.startPage(dto.getPageNum(), dto.getPageSize(),false);
    在这里插入图片描述
  • 开启查询总数
    PageMethod.startPage(dto.getPageNum(), dto.getPageSize());
    在这里插入图片描述
    count(*)与数据量关系

InnoDB存储引擎(以 MySQL 为例)

数据量越多查询速度越慢,原因需要读取大量的数据页,涉及更多的磁盘 I/O 操作和 CPU 计算所以比较耗时

31条数据查询0.1s
在这里插入图片描述

500w条数据查询约6.4s
在这里插入图片描述
所以是否开启分页,要考虑数据量的影响。

参考:
https://pagehelper.github.io/docs/
分页插件PageHelper工作原理_pagehelper底层原理
使用PageHelper实现分页查询
PageHelper原理深度剖析(集成+源码)
彻底搞懂MyBatis插件原理及PageHelper原理
分页查询-PageHelper底层原理分析以及使用PageHelper的步骤

看到这里的友友们 点个赞吧👍👍👍
🍉文章不定期持续更新

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值