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 会过滤掉无分类的数据。

相关推荐
Irene1991几秒前
Java 3 天入门:“最小必要知识”的功利性学法
java
长谷深风1118 分钟前
Agent 何时该 Replan:五个关键判断
java·大数据·开发语言·ai agent·ai智能体·agent设计·clarify机制
lingran__14 分钟前
C++ STL unordered系列(哈希) 底层剖析与模拟实现万字详解 | 基于哈希表,复刻 SGI-STL 泛型哈希容器架构
开发语言·c++·后端·哈希算法·哈希表·泛型编程·unordered系列
阿弱31 分钟前
graph-core 的边与命令模式设计
java·后端·agent
互联网中的一颗神经元40 分钟前
01. Go 内存管理全景架构
java·jvm·golang
萧瑟余晖41 分钟前
Java深入解析篇三十四之分布式事务
java·开发语言·分布式
CAE虚拟与现实42 分钟前
源码注解 /class 注解 / 运行时注解三者生命周期
java·开发语言
有脚就行44 分钟前
第24篇-Go-gRPC推理服务-高性能跨语言通信
开发语言·人工智能·后端·golang
码匠许师傅44 分钟前
【C++ 面试真题】聊聊 C++ 的关联容器
开发语言·c++·面试