1. 分页查询基础概念与原生实现
在Web应用开发中,分页查询是最基础也最常用的功能之一。想象一下,当用户查询数据库中有上万条记录时,如果一次性全部返回给前端,不仅会造成网络传输压力,还会严重影响用户体验。这就好比在图书馆找书——管理员不会把整个图书馆的书都搬给你,而是根据你的需求每次拿几本给你查阅。
1.1 分页的核心要素
一个完整的分页功能需要包含以下核心参数:
- 当前页码(pageNo):用户请求的是第几页数据
- 每页数量(pageSize):每页展示多少条记录
- 总记录数(total):符合条件的数据总量
- 数据列表(list):当前页的实际数据集合
在MySQL等关系型数据库中,通常使用LIMIT offset, size语法实现分页,其中offset的计算公式为:
java复制int offset = (pageNo - 1) * pageSize;
1.2 原生分页实现详解
1.2.1 三层架构分工
在传统SpringBoot项目中,分页功能通常按照MVC三层架构实现:
Controller层:
java复制@PostMapping("/rooms")
public PageResult<Room> queryRooms(
@RequestParam(defaultValue = "1") int pageNo,
@RequestParam(defaultValue = "10") int pageSize,
@RequestBody RoomQuery query) {
return roomService.queryRooms(pageNo, pageSize, query);
}
Service层核心逻辑:
java复制public PageResult<Room> queryRooms(int pageNo, int pageSize, RoomQuery query) {
// 计算起始行
int offset = (pageNo - 1) * pageSize;
// 查询当前页数据
List<Room> list = roomMapper.selectByQuery(query, offset, pageSize);
// 查询总数
long total = roomMapper.countByQuery(query);
return new PageResult<>(list, total);
}
Mapper层SQL示例:
xml复制<select id="selectByQuery" resultType="Room">
SELECT * FROM room
<where>
<if test="query.name != null">
AND name LIKE CONCAT('%', #{query.name}, '%')
</if>
</where>
LIMIT #{offset}, #{pageSize}
</select>
<select id="countByQuery" resultType="long">
SELECT COUNT(*) FROM room
<where>
<if test="query.name != null">
AND name LIKE CONCAT('%', #{query.name}, '%')
</if>
</where>
</select>
1.2.2 原生实现的痛点
虽然这种方式能实现基本分页需求,但在实际项目中会暴露几个明显问题:
- 代码重复:每个分页方法都需要编写几乎相同的分页逻辑
- 维护困难:当需要修改分页逻辑时,需要改动所有相关方法
- 容易出错:手动计算offset容易出错,特别是复杂查询场景
- 功能单一:缺少对复杂分页需求的支持,如排序、多表关联等
经验之谈:在中小型项目中,如果分页需求简单且数量不多,原生实现尚可接受。但当项目规模扩大、分页需求复杂化后,这种方式的维护成本会呈指数级增长。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. PageHelper插件深度解析
2.1 PageHelper核心原理
PageHelper是国内最流行的MyBatis分页插件,其核心原理可以概括为:
- 拦截器机制:通过实现MyBatis的Interceptor接口,在SQL执行前进行拦截
