目录

一.问题引入:10000 条用户数据怎么显示?

二.什么是数据分页

三.源码分析

3.1前端分析

3.2后端分析

3.2.1startPage

 3.2.1.1PageHelper 实现

3.2.1.2PageHelper 工作原理

3.2.2getDataTable(list)

四.总结

五.查看第二页的数据

5.1后端返回的log


一.问题引入:10000 条用户数据怎么显示?

想象一下,你的系统中有 10,000 个用户。如果前端一次性请求全部数据并渲染到页面上,会带来哪些问题?

  • 前端内存压力大:浏览器需加载并处理大量 DOM 元素,可能导致卡顿甚至崩溃。
  • 网络传输慢:一次性传输大量数据占用带宽,响应时间长。
  • 用户体验差:用户需要滚动很久才能找到目标信息。
  • 后端资源消耗高:数据库查询全表、应用服务器内存占用激增。

因此,数据分页(Pagination) 成为解决大数据量展示的标准方案。

二.什么是数据分页

数据分页是将大量数据按固定数量拆分成分批(页),按需加载 / 展示的技术,核心是避免一次性处理全部数据。

三.源码分析

3.1前端分析

页面中是如何渲染

前端数据获取

向后端发送请求

3.2后端分析

看前端请求后端的url,和上面的请求后端的url一致

找到并分析后端代码controller层中对应的接口申明'/system/post/list'

这里主要使用startPage和getDataTable(list)实现分页查询

3.2.1startPage

 3.2.1.1PageHelper 实现

PageHelper.startPage(pageNum, pageSize, orderBy).setReasonable(reasonable);

  • 这是 PageHelper 插件的核心 API,作用是:初始化分页上下文,告诉 MyBatis「接下来的一次 SQL 查询需要分页」。
  • PageHelper.startPage(...)
    • 第一个参数 pageNum:当前页码;
    • 第二个参数 pageSize:每页条数;
    • 第三个参数 orderBy:排序规则(已做防注入处理),会自动拼接进 SQL 的 ORDER BY 子句;
    • 该方法返回一个 Page 对象,用于配置额外分页规则(如 reasonable)。
  • .setReasonable(reasonable):启用 / 禁用分页合理性校验(对应前面的 reasonable 参数)。
3.2.1.2PageHelper 工作原理

这段代码执行后,后续执行的 MyBatis Mapper 查询方法 会被 PageHelper 拦截,自动完成 2 件事:

  1. 对查询 SQL 拼接分页语法(如 MySQL):
    • 原 SQL:SELECT * FROM sys_user WHERE status = 1
    • 拦截后 SQL:SELECT * FROM sys_user WHERE status = 1 ORDER BY create_time DESC LIMIT 0, 10(pageNum=1,pageSize=10 时,LIMIT 起始索引 = 0,条数 = 10);
  2. 自动查询总条数:PageHelper 会额外执行一条 COUNT(*) 语句(如 SELECT COUNT(*) FROM sys_user WHERE status = 1),用于计算总数据量 total,后续会返回给前端,让前端分页组件计算总页数。

调用时机startPage() 必须在 MyBatis 查询方法执行前 调用(紧挨着查询方法),否则分页失效。

3.2.2getDataTable(list)

rspData.setTotal(new PageInfo(list).getTotal());关键步骤:通过 PageInfo 从 list 中提取总数据条数 total

将 PageHelper 处理后的 (page继承于list) 封装成 PageInfo 对象,从 PageInfo 中获取总数据条数。

Page<E>(分页数据载体)

  • 继承自 ArrayList<E>,是 PageHelper 插件中承载当前页数据的核心类。它不仅存储当前页的列表数据(如查询到的 10 条用户信息),还隐藏了分页元数据(如总条数 total、当前页码 pageNum 等)。
  • 作用:让分页后的列表可以直接以 List 类型返回(多态特性),同时通过 PageInfo 可提取分页元数据。

四.总结

数据分页过程

  1. 调用 PageHelper.startPage(pageNum, pageSize) 后,插件会在 ThreadLocal 中存储分页参数;
  2. 执行 MyBatis 查询时,插件拦截 SQL,自动拼接 LIMIT 并查询总条数;
  3. 查询结果被封装为 Page<E>(继承 ArrayList),既包含当前页数据,也隐含总条数等元数据;
  4. 前端通过 PageInfo 提取 total(总条数),结合 Page 中的列表数据,实现分页渲染。

五.查看第二页的数据

5.1后端返回的log

21:23:43.754 [http-nio-8080-exec-20] DEBUG c.r.s.m.S.selectOperLogList_COUNT - [debug,135] - ==>  Preparing: SELECT count(0) FROM sys_oper_log
21:23:43.754 [http-nio-8080-exec-20] DEBUG c.r.s.m.S.selectOperLogList_COUNT - [debug,135] - ==> Parameters: 
21:23:43.756 [http-nio-8080-exec-20] DEBUG c.r.s.m.S.selectOperLogList_COUNT - [debug,135] - <==      Total: 1
21:23:43.757 [http-nio-8080-exec-20] DEBUG c.r.s.m.S.selectOperLogList - [debug,135] - ==>  Preparing: select oper_id, title, business_type, method, request_method, operator_type, oper_name, dept_name, oper_url, oper_ip, oper_location, oper_param, json_result, status, error_msg, oper_time, cost_time from sys_oper_log order by oper_id desc LIMIT ?, ?
21:23:43.758 [http-nio-8080-exec-20] DEBUG c.r.s.m.S.selectOperLogList - [debug,135] - ==> Parameters: 10(Long), 10(Integer)
21:23:43.760 [http-nio-8080-exec-20] DEBUG c.r.s.m.S.selectOperLogList - [debug,135] - <==      Total: 10
  • Parameters: 10(Long), 10(Integer)LIMIT 的两个参数含义:
    • 第一个参数 10查询起始索引 = (pageNum - 1) * pageSize(计算可得:pageNum=2pageSize=10 → (2-1)*10=10)。
    • 第二个参数 10每页条数 = pageSize(对应前端 queryParams.pageSize=10)。
    • 结论:当前查询的是「第 2 页,每页 10 条数据」。
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐