🍞文章有不合理的地方请各位大佬指正。
🍉文章不定期持续更新,如果我的文章对你有帮助➡️ 关注🙏🏻 点赞👍 收藏⭐️
直接上代码,下面是常见的分页应用代码。
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:定义了一个拦截器,用于拦截MyBatis的Executor类中的query方法。 - 注解
@Signature:指定了要拦截的方法签名,包括方法类型、方法名和参数类型。 - 可以看出这里拦截了
Executor中6参数query查询, 4参数query查询 PageInterceptor中的intercept方法是整个分页查询的关键,核心代码如下:  流程图  围绕这块核心代码分析一下:
- 判断是否需要进行 count 查询
dialect.beforeCount(ms, parameter, rowBounds)
//默认开启
this.count = true PageMethod.startPage(dto.getPageNum(), dto.getPageSize());
// 关闭查询总数
PageMethod.startPage(dto.getPageNum(), dto.getPageSize(),false);
-
查询总数
Long count = count(executor, ms, parameter, rowBounds, null, boundSql);执行自动创建的 count 查询获取查询总数的
sql将查询的
sql包起来,拼装count(sql),查询总数。 -
执行查询
resultList = ExecutorUtil.pageQuery(dialect, executor, ms, parameter, rowBounds, resultHandler, boundSql, cacheKey);
-
sql如何拼接limit?,?
获取
limit,拼接到查询sql尾部
- 分页查询后,处理分页结果集
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
所以是否开启分页,要考虑数据量的影响。
参考:
分页插件PageHelper工作原理_pagehelper底层原理
分页查询-PageHelper底层原理分析以及使用PageHelper的步骤
看到这里的友友们 点个赞吧👍👍👍