java: 分页查询(自用笔记)

1. Controller 层

1.1 示例代码(含详细备注)

复制代码
@GetMapping("/admin/dish/page")
public Result<PageResult> page(DishPageQueryDTO dto) {

    /*
     * 说明:
     * 1. Spring MVC 会自动将 Query 参数绑定到 dto 中
     *    例如:/page?page=1&pageSize=10&name=鱼
     *    dto.page = 1
     *    dto.pageSize = 10
     *    dto.name = "鱼"
     *
     * 2. Controller 不处理业务,仅调用 service 层
     */

    PageResult result = dishService.page(dto);

    // 使用统一响应结构
    return Result.success(result);
}

1.2 关键细节解释

  1. 参数封装方式

    • Spring 自动将 URL 查询参数映射到 DTO 中(按字段名匹配)。

    • 同名参数会被完整填充,无需使用 @RequestParam。

  2. Controller 不执行分页逻辑

    • 分页逻辑完全放在 service 层,使 Controller 保持轻量与无业务状态。
  3. 返回类型使用 Result

    • Result 为统一响应包装类。

    • PageResult 存储 total 和 records(分页数据)。

  4. DTO 字段规则

    DTO 的字段名必须与前端传参保持一致,如 page、pageSize、name 等,Spring 才能自动封装。


1.3 问题总结

  1. Query 参数能否自动封装为对象?

    可以,只需参数名与 DTO 字段名一致。

  2. 是否必须用 @RequestParam?

    不是。分页查询使用 DTO 封装更规范。

  3. Controller 能否直接调用 PageHelper?

    不建议。分页逻辑属于业务层,由 service 层统一处理。


2. ServiceImpl 层

2.1 示例代码(含详细备注)

复制代码
@Override
public PageResult page(DishPageQueryDTO dto) {

    /*
     * PageHelper.startPage 必须放在查询前执行。
     * 内部原理:
     * 1. 创建 Page 对象并绑定到 ThreadLocal
     * 2. 拦截接下来执行的 SQL
     * 3. 自动拼接 limit offset
     * 4. 自动执行 count 查询获取总数
     */
    PageHelper.startPage(dto.getPage(), dto.getPageSize());

    // 执行 Mapper 查询。此时 SQL 会被分页插件自动改写。
    List<DishVO> list = dishMapper.page(dto);

    /*
     * PageInfo 的作用:
     * 1. 识别 list 是否为 Page 类型
     * 2. 自动读取 total 和当前页数据
     */
    PageInfo<DishVO> pageInfo = new PageInfo<>(list);

    // 封装为通用分页返回结构
    return new PageResult(pageInfo.getTotal(), pageInfo.getList());
}

2.2 关键细节解释

  1. PageHelper.startPage 的作用

    创建分页上下文,使下一条 SQL 自动拼接分页语句。

    offset = (page - 1) * pageSize

  2. Page 对象与 PageInfo 的关联

    • list 在 PageHelper 处理后会成为 Page 类型

    • PageInfo 可以从 list 中读取分页信息(total, records)

  3. PageHelper 必须在执行 SQL 之前调用

    如果写在 SQL 之后,则不会生效。

  4. 返回统一的 PageResult

    PageResult 包含:

    • total:总条数

    • records:当前页数据列表


2.3 常见问题总结

  1. PageHelper.startPage 是否会执行 SQL?

    不执行,只是设置上下文。

  2. PageHelper 如何知道要分页哪条 SQL?

    startPage 与下一条 mapper 查询绑定,通过拦截器实现。

  3. PageInfo 必须 new 吗?

    是的,用于自动解析分页数据。

  4. ServiceImpl 是否需要 try-catch?

    可选,按项目统一规范处理。


3. Mapper XML 层

3.1 示例 SQL(含详细备注)

复制代码
<select id="page" resultType="com.sky.vo.DishVO">

    /*
     * 说明:
     * 1. 通过 LEFT JOIN 关联 category 表,获取分类名称
     * 2. 使用别名将不一致字段映射到 VO 字段
     * 3. 动态构建 WHERE 条件
     */

    SELECT
        d.id,
        d.name,
        d.price,
        d.status,

        -- 数据库字段 update_time 与 VO 字段 updateTime 不同,必须指定别名
        d.update_time AS updateTime,

        -- 分类名称来自 category 表
        c.name AS categoryName

    FROM dish d
    LEFT JOIN category c ON d.category_id = c.id

    <where>
        <!-- 模糊搜索 name -->
        <if test="name != null and name != ''">
            AND d.name LIKE CONCAT('%', #{name}, '%')
        </if>

        <!-- 分类过滤 -->
        <if test="categoryId != null">
            AND d.category_id = #{categoryId}
        </if>

        <!-- 状态过滤 -->
        <if test="status != null">
            AND d.status = #{status}
        </if>
    </where>

    ORDER BY d.update_time DESC
</select>

3.2 关键细节解释

  1. JOIN 逻辑说明

    菜品表不包含 categoryName,因此需要关联 category 表的 name 字段。

  2. 字段别名规则

    • 当数据库字段与 VO 字段名不同,必须使用 AS

    • MyBatis 使用字段名匹配映射(非位置匹配)

  3. 标签处理逻辑

    • 自动添加 WHERE

    • 自动剔除多余 AND/OR

    • 若所有 条件均不成立,则不会生成 WHERE

  4. 动态 SQL

    根据 DTO 字段是否为 null 或空字符串决定是否拼接 SQL 条件。


3.3 常见问题总结

  1. 为什么能自动去掉第一个 AND?

    因为 MyBatis 会对生成的 SQL 进行修剪处理。

  2. 字段名不一致为什么需要 AS?

    MyBatis 使用字段名与 VO 字段名直接匹配,不一致不会自动映射。

  3. 查询时 DTO 字段为 null 会怎样?

    不会拼接对应 SQL 片段。

  4. 是否必须使用 LEFT JOIN?

    若希望无分类的菜品也能展示,则使用 LEFT JOIN;否则 INNER JOIN 会过滤掉无分类的数据。

相关推荐
enjoy编程1 小时前
Spring-AI 利用KeywordMetadataEnricher & SummaryMetadataEnricher 构建文本智能元数据
java·人工智能·spring
繁华似锦respect1 小时前
lambda表达式中的循环引用问题详解
java·开发语言·c++·单例模式·设计模式·哈希算法·散列表
我要升天!1 小时前
QT -- 网络编程
c语言·开发语言·网络·c++·qt
Unlyrical1 小时前
为什么moduo库要进行线程检查
linux·服务器·开发语言·c++·unix·muduo
GIS阵地1 小时前
Qt实现简易仪表盘
开发语言·c++·qt·pyqt·qgis·qt5·地理信息系统
heartbeat..1 小时前
介绍一下软件开发中常见的几种的架构模式
java·架构·开发
天天摸鱼的小学生1 小时前
【Java Enum枚举】
java·开发语言
阿猿收手吧!1 小时前
【C++】cpp虚函数和纯虚函数的声明和定义
开发语言·c++