rowbounds是mybatis原生逻辑分页,先查全量数据再内存截取,仅适用于≤500行的小数据集;物理分页需sql显式加limit/offset等,性能优、内存省,生产环境必须使用。

MyBatis 原生的 RowBounds 确实能实现分页,但它不是数据库物理分页,而是查询全部结果后在内存中截取指定范围——这在数据量大时极不推荐,会严重拖慢性能、消耗 JVM 内存。所谓“低成本逻辑内存分页”,本质是妥协方案,只适用于小数据集(如几百条以内)或后台管理类低频、非核心接口。
RowBounds 的真实工作原理
RowBounds 不改写 SQL,也不加 LIMIT/OFFSET。它让 MyBatis 在拿到完整 ResultSet 后,用 skip 和 limit 跳过前 N 条、取 M 条,全程在 Java 堆内存里操作 List。这意味着:
- 数据库仍返回全部记录(比如查 10 万行,只显示第 101–110 行,其余 99990 行白加载)
- GC 压力增大,尤其对象复杂时易触发 Full GC
- 网络传输和序列化开销翻倍
正确使用 RowBounds 的前提条件
若坚持不用插件(如 PageHelper),又想用 RowBounds,必须满足以下全部条件:
- 对应表数据总量稳定且≤500 行(例如系统配置表、状态字典表)
- SQL 已通过 WHERE 精准过滤,返回结果集本身就很小
- 业务允许首次加载稍慢(比如页面初始化时一次性拉全再前端分页)
- 明确告知调用方:这不是真分页,仅作简易展示用途
代码示例与关键细节
Mapper 接口方法无需改动,保持原始签名:
public List<user> selectAllUsers();</user>
调用时传入 RowBounds:
RowBounds rowBounds = new RowBounds(20, 10); // offset=20, limit=10<br>List<user> list = userMapper.selectAllUsers(rowBounds);</user>
注意:
-
offset是跳过的行数,不是页码;页码需自行换算:offset = (pageNum - 1) * pageSize - Mapper XML 中 不能 写
<if test="rowBounds != null"></if>这类判断——RowBounds不参与 SQL 参数绑定 - 返回值必须是
List<t></t>,不能是单个对象或 Map - 无法获取总记录数(count),需额外执行一次
select count(*)
比 RowBounds 更稳妥的轻量替代方案
若连 PageHelper 都不能引入,又希望真正降低数据库压力,可手动拼 LIMIT(需数据库支持):
- MySQL:在 SQL 中显式写
limit #{offset}, #{limit},参数由 service 层计算传入 - PostgreSQL:用
limit #{limit} offset #{offset} - Oracle 12c+:用
OFFSET #{offset} ROWS FETCH NEXT #{limit} ROWS ONLY - 同时配套写一个
count查询,用于计算总页数
这种方式虽多写一条 SQL,但避免了全量加载,成本远低于 RowBounds,且不依赖第三方。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











