深入解析数据分页:从源码到实战 - 指南

目录

第一部分:什么是材料分页,为什么要进行信息分页?

第二部分:源码分析——以操作日志页面为例

第一步:前端(Vue 页面)

定义前端发给后端的“信使-参数”

后端回信:总条数-total

用户运行:点击上一页或下一页等触发getList()方法

那么用户操作在哪里展示了?——分页组件(pagination)

小结

第二步:网络拦截

第三步:后端分页处理

根据接口找到后端代码——ISysOperlogController.java

​ 第一步:开启 startPage() —— 从 HTTP 到 ThreadLocal

第二步: 执行查询:selectOperLogList

第三步:包装结果 getDataTable —— 取出 total

第四步总结:

第三部分:源码分析——以若依平台中字典管理为例

第一步:前端分页处理(Vue页面)

定义前端发给后端的“信使-参数”

后端回信:总条数-total

用户操作:点击上一页或下一页等触发getList()方式

那么用户操作在哪里展示了?——分页组件(pagination)

第二步:网络拦截

第三步:后端分页处理

第四步:总结

第四部分:模仿类Page,自定义一个类MyPage

1、创建 MyPage 类:继承 ArrayList,增加 total 属性

2、在 Service 层添加 selectNameList()

3、在 Controller 中暴露接口

4、改造 BaseController.getDataTable()(关键!)

5、测试


第一部分:什么是数据分页,为什么要进行数据分页?

场景:

  • 想象你有一本厚达10,000 页的百科全书(数据库里有 1 万条日志数据)。

材料分页?就是1. 什么

  • “不要一次性把整本书的内容全摊开在桌子上”就是数据分页就
    而是“一次只读一页”。
  • 在软件里,我们告诉框架:“我要看第1页,每页展示10 条数据”。
    系统就会只给你拿第 1 到第 10 条,而不是把 1 万条全扔给你。

2. 为什么要进行数据分页?

  • 性能(后端 & 数据库):假设数据库里100万条执行日志,若是不分页一次性全查出来,数据库会累死(查询慢),服务器内存会撑爆(OOM),系统直接卡死
  • 体验(前端&用户):如果你在浏览器里一次性渲染100万行表格,浏览器会直接崩溃白屏。而且用户也不能一次性看完100万条数据,他们也是一页一页看的
  • 流量(网络):传输10条数据可能只要10KB,传输100万条数据可能要几百MB。分页行节省大量的网络带宽

第二部分:源码分析——以操作日志页面为例

第一步:前端(Vue 页面)

  • 位置:找到操作日志的前端代码档案(index.vue)
  • 目标:用哪个变量来接收后端返回的total的就是看看前端在“发请求”时带了什么参数(Eg:pageNum-页面编号,pageSize-页面大小), 以及前端
定义前端发给后端的“信使-参数”

这两个变量是核心,他们决定了你要看那一部分数据

后端信:总条数-total

用户操作:点击上一页或下一页等触发getList()方法

  • 前端得知总数的唯一途径就是关键:this.total = response.total;——这
  • 思路流程图:

那么用户操作在哪里展示了?——分页组件(pagination

  • 当什么情况发生时触发?
    • 用户点击“上一页/下一页”
    • 用户修改“每页条数”
    • 用户跳转到某一页
  • 触发后做什么?
    • 执行 getList() 方法
    • 发起新的请求,获取新页面的数据

整个流程:改变参数 → 触发请求 → 更新信息

小结
  • 前端其实很便捷,它只负责“提要求”(我要第2页,10条数据)和“展示结果怎么算出来的,前端完全不知道,全靠后端给。就是”(渲染表格,根据total渲染页码)。至于78条总数
  • 补充:Vue 小技巧:.sync 是一个魔法修饰符。意思是:当你在界面上点击“下一页”时,组件会自动修改 queryParams.pageNum 的值,然后通过 pagination="getList" 再次触发查询。

第二步:网络拦截

第三步:后端分页处理

根据接口找到后端代码——ISysOperlogController.java

第一步:开启 startPage() —— 从 HTTP 到 ThreadLocal

疑问:参数pageNum和pageSize去哪里了?没有看到有代码接收他们

解决:(代码追踪)

  1. Controller层(ISysOperlogController.java
  2. 父类BaseController(BaseController.java)——[ctrl+startPage+鼠标]
  3. 应用类(PageUtils.java)
    行号代码作用
    TableSupport.buildPageRequest()从当前 HTTP 请求中提取 pageNumpageSize 参数
    → 来自前端 URL:?pageNum=2&pageSize=10
    pageDomain.getPageNum()获取当前页码(如 2)
    pageDomain.getPageSize()获取每页大小(如 10)
    isNotNull(pageNum) && isNotNull(pageSize)判断参数是否有效,防止空值导致异常
    SqlUtil.escapeOrderBySql(...)安全处理排序字段(防 SQL 注入),比如将 desc 转成 DESC
    pageDomain.getReasonable()是否开启“合理化分页”(默认 true)
    → 如果用户输入 pageNum=999, pageSize=10,但总记录只有 50 条,则自动跳转到第 5 页
    PageHelper.startPage(...)关键!调用 PageHelper 核心方式,开启分页拦截
第二步: 执行查询:selectOperLogList

疑问:为什么只写了查询所有,它却知道要去查 total 并分页?

解决:(代码追踪)

  1. Service层——把任务直接交给Mapper层,JVM并没有直接执行java办法,而是调用了Mybatis生成的代理对象
  2. Mapper层——准备要去执行SQL
  3. 拦截器——PageHelper 插件—— PageInterceptor.java(在 jar 包里)
  4. 分析日志
    pageHelper计算总数:
    查询数据(The Query):

第三步:包装结果 getDataTable —— 取出 total

疑问:getDataTable 是怎么找到 total

解决:(代码追踪)

  1. BaseController.java
  2. 最后,rspData 这个对象被转换成 JSON:

    前端收到JSON对象,任务闭环

第四步总结:


第三部分:源码分析——以若依系统中字典管理为例

——和第二部分一样

第一步:前端分页处理(Vue页面)

定义前端发给后端的“信使-参数”

后端信:总条数-total

用户管理:点击上一页或下一页等触发getList()方法

那么用户操作在哪里展示了?——分页组件(pagination

第二步:网络拦截

第三步:后端分页处理

根据接口找到后端代码——ISysOperlogController.java

  1. 开启分页startPage()
  2. 执行查询
  3. 包装结构

这三部分的深入解析和第二部分的完全一样(所以这里不做重复赘述)

第四步:总结


第四部分:模仿类Page,自定义一个类MyPage

1、创建 MyPage:继承 ArrayList,增加 total 属性

  • 路径:ruoyi-common/src/main/java/com/ruoyi/common/core/page/MyPage.java

2、在 Service 层添加 selectNameList()

  • 路径:ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java

同时在接口中声明(ISysUserService.java):

3、在 Controller 中暴露接口

⚠️ 注意:必须用 getDataTable(list),因为它是提取 total 的关键!

4、改造 BaseController.getDataTable()(关键!)

打开:ruoyi-common/src/main/java/com/ruoyi/common/core/controller/BaseController.java

5、测试

posted @ 2025-12-19 10:18  gccbuaa  阅读(2)  评论(0)    收藏  举报