彻底吃透若依分页原理|分层职责、底层源码、手写分页、无参默认分页

彻底吃透若依分页原理|分层职责、底层源码、手写分页、无参默认分页

前言

在使用若依(RuoYi)开发过程中,分页是最高频的功能。大部分人只会固定模板:startPage\(\)getDataTable\(\),但是并不清楚:

  • 为什么Service层不需要写分页代码

  • PageHelper底层到底怎么拦截SQL?

  • 如果不使用插件,自己手写分页怎么实现

  • 前端不传pageNum、pageSize为什么也能分页?

本文通俗易懂、纯实战角度,彻底吃透若依分页。


一、若依标准分页代码(最简模板)

1. 控制层 Controller(负责分页、封装返回)

控制层是分页的唯一入口

@GetMapping("/commission/list")
public TableDataInfo commissionList() {
    startPage(); // 1.开启分页
    List<CommissionSubVO> list = appUserService.getMyCommissionList(); // 2.查询数据
    return getDataTable(list); // 3.封装分页结果
}

2. 业务层 Service(只做查询,不管分页)

Service层没有任何分页逻辑,只负责业务查询。

@Service
public class AppUserServiceImpl implements AppUserService {

    @Resource
    private CommissionMapper commissionMapper;

    @Override
    public List<CommissionSubVO> getMyCommissionList() {
        // 直接返回mapper原生查询结果,不做集合处理
        return commissionMapper.getMyCommissionList();
    }
}

3. Mapper层

List<CommissionSubVO> getMyCommissionList();

二、三层架构分页职责(核心思想,必须背)

1. 分层职责

  • Controller:管分页、管参数、管返回

    • 解析分页参数

    • 开启分页

    • 封装前端需要的格式(total、rows)

  • Service:管业务、管查询、不管分页

    • 只查询原始数据

    • 绝对不能修改返回集合

  • Mapper:纯粹执行SQL

2. 硬性铁律(分页失效99%都是违反这个)

startPage() 后面必须紧跟第一条mapper查询,中间不能有任何代码、不能二次处理list。


三、若依分页底层完整执行原理

1. 核心依赖

  • PageHelper:MyBatis拦截器,分页核心

  • ThreadLocal:线程隔离,存放分页参数

  • startPage():解析参数、存入线程

  • getDataTable():解析Page对象,封装返回

2. 完整执行流程

  1. 前端传入:pageNum=1\&amp;pageSize=10

  2. 执行 startPage\(\):解析参数放入 ThreadLocal

  3. 执行mapper查询,PageHelper拦截SQL

  4. 插件自动执行两条SQL:

    • select count\(\*\):查询总条数

    • select \.\.\. limit ?,?:拼接分页SQL

  5. 结果封装为 Page对象(继承ArrayList,外表是List,内部携带total)

  6. getDataTable\(\) 提取total、rows,返回前端


四、进阶:前端不传pageNum、pageSize如何分页?

1. 场景

前端请求:/commission/list,不带任何分页参数,依然正常分页。

2. 源码原理(若依自带默认值)

startPage\(\) 内部做了判空处理:

// 不传参数给默认值
if (pageNum == null) {
    pageNum = 1;
}
if (pageSize == null) {
    pageSize = 10;
}
// 防止恶意大数据量查询
pageSize = Math.min(pageSize, 1000);

3. 默认规则

  • 默认页码:pageNum = 1

  • 默认条数:pageSize = 10

  • 最大限制:pageSize ≤ 1000


五、拔高:不使用PageHelper,手写原生分页

面试必问:不借助插件,如何手动实现分页?

1. 分页核心公式

起始下标 = (pageNum - 1) * pageSize

2. 手写完整代码

① 分页参数实体

@Data
public class PageDomain {
    private Integer pageNum = 1;
    private Integer pageSize = 10;
}

② Controller

@GetMapping("/my/list")
public TableDataInfo myPageList(PageDomain pageDomain){
    return appUserService.myPageList(pageDomain);
}

③ Service(真正手写分页逻辑)

@Override
public TableDataInfo myPageList(PageDomain pageDomain) {
    // 1.计算起始位置
    int start = (pageDomain.getPageNum() - 1) * pageDomain.getPageSize();
    // 2.查询总条数
    Long total = commissionMapper.selectCount();
    // 3.查询当前页数据
    List<CommissionSubVO> list = commissionMapper.selectPageList(start,pageDomain.getPageSize());

    // 4.手动封装返回
    TableDataInfo info = new TableDataInfo();
    info.setTotal(total);
    info.setRows(list);
    return info;
}

④ Mapper XML

<!-- 查询总数 -->
<select id="selectCount" resultType="long">
    select count(*) from commission
</select>

<!-- 手动分页查询 -->
<select id="selectPageList" resultType="com.xxx.vo.CommissionSubVO">
    select * from commission
    limit #{start},#{pageSize}
</select>

3. 手写分页总结

  • 原理:一条查总数、一条查分页

  • 缺点:重复代码多、每一个列表都要写两条SQL

  • PageHelper 就是把这一段代码自动化了


六、全文终极总结(背诵版)

  1. 若依分页:Controller管分页,Service管数据,插件管SQL

  2. Service层不能修改返回集合,否则Page分页信息丢失。

  3. PageHelper依靠ThreadLocal存储参数,只对下一条查询生效。

  4. 前端不传分页参数,若依自动赋予默认值。

  5. 手写分页核心公式:起始下标=\(页码\-1\)\*条数


七、通俗易懂口诀

控制层开启分页,业务层干净查询;

插件拦截拼接SQL,不传参数默认十;

手写分页两条库,Limit公式要熟记。

(注:文档部分内容可能由 AI 生成)

posted @ 2026-05-09 22:31  白鹿为溪  阅读(96)  评论(0)    收藏  举报