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

相关推荐
xyq20245 分钟前
Java 变量命名规则
开发语言
天启HTTP6 分钟前
HTTP代理和隧道代理的底层区别与适用场景分析
开发语言·网络协议·tcp/ip·php
清心歌7 分钟前
HashMap实现原理及扩容机制
java
一只大袋鼠7 分钟前
数据库连接池从入门到精通(下):Druid 连接池使用与工具类封装
java·数据库·连接池
小白学大数据7 分钟前
告别复杂 XPath:DeepSeek+Python 爬虫快速实践
开发语言·爬虫·python·selenium
禹中一只鱼10 分钟前
【IDEA 出现 `IDE error occurred`】
java·ide·spring boot·intellij-idea
西凉的悲伤10 分钟前
Guava类库——Lists.partition() 高效分批处理列表数据
java·guava
weixin_4080996711 分钟前
【保姆级教程】按键精灵调用 OCR 文字识别 API(从0到1完整实战 + 可运行脚本)
java·前端·人工智能·后端·ocr·api·按键精灵
brahmsjiang12 分钟前
Java类加载机制解析:从JVM启动到双亲委派,再到Android的特殊实现
android·java·jvm
fengci.14 分钟前
LilCTF2025web(前半部分)
开发语言·网络·学习·php