MyBatis RowBounds原理
ps:如果这篇帖子对于还在找工作和找实习的你有所帮助,可以关注我,给本贴点赞、评论、收藏并订阅专栏;同时不要吝啬您的花花
一、核心定位:原生逻辑分页(内存分页)
RowBounds是MyBatis内置的轻量分页工具,核心本质是逻辑分页(内存分页),而非数据库层面的物理分页。它不会改写SQL语句、不会生成LIMIT/OFFSET等数据库分页关键字,而是先执行全量SQL查询获取完整结果集,再在Java内存中对结果集做截取筛选,最终返回指定范围的分页数据。
这种分页方式无需修改Mapper XML、无需额外配置,仅通过传入RowBounds对象即可实现分页,具备零侵入、易使用的特点,但也存在明显的性能短板。
二、核心参数与基础用法
2.1 核心参数
RowBounds仅包含两个核心整型参数,精准控制分页范围:
- offset:偏移量,代表从结果集的第几条记录开始截取(默认值为0,从第一条开始)
- limit:限制数,代表每页最多返回的记录条数(默认值为Integer.MAX_VALUE,返回全部数据)
2.2 基础调用方式
在调用Mapper接口方法时,直接传入RowBounds对象即可,SQL语句无需做任何改动:
// 构建分页对象:偏移量0,每页10条 RowBounds rowBounds = new RowBounds(0, 10); // 调用Mapper查询,自动实现分页 List<User> userList = userMapper.selectAllUser(rowBounds);
对应的Mapper XML仍为普通全量查询,无需添加分页条件:
<select id="selectAllUser" resultType="com.demo.entity.User">
SELECT id,username,age FROM user
</select>
三、底层执行全流程
RowBounds的分页逻辑贯穿查询全链路,每一步都围绕「内存截取」展开,具体流程如下:
- 参数接收:MyBatis执行器接收到带有RowBounds参数的查询请求,解析offset和limit值
- 全量SQL执行:向数据库发送未改写的原始全量SQL,数据库返回符合条件的所有数据(ResultSet结果集)
- 结果集遍历筛选:MyBatis底层处理器遍历ResultSet,先跳过offset条前置数据,再读取最多limit条数据
- 封装返回:将筛选后的少量数据封装为List集合返回,丢弃剩余未选中数据
关键结论:无论查询第几页,数据库都会执行全表/全条件查询,分页筛选完全在Java应用内存中完成,这是RowBounds与物理分页的核心区别。
四、源码核心实现逻辑
RowBounds的分页筛选逻辑,主要在MyBatis的DefaultResultSetHandler(结果集处理器)中实现,核心源码片段解析如下:
// 核心方法:处理简单结果集的分页逻辑
private void handleRowValuesForSimpleResultMap(...) throws SQLException {
ResultSetWrapper rsw = getFirstResultSet(stmt);
List<Object> values = new ArrayList<>();
int rowNum = 0;
// 遍历结果集
while (shouldProcessMoreRows(resultContext, rowBounds) && rsw.getResultSet().next()) {
// 跳过偏移量之前的数据
if (rowNum < rowBounds.getOffset()) {
rowNum++;
continue;
}
// 读取限制条数内的数据
if (values.size() < rowBounds.getLimit()) {
Object rowValue = getRowValue(rsw, mappedStatement, resultHandler, resultContext, rowBounds);
values.add(rowValue);
}
rowNum++;
}
// 返回筛选后的分页数据
callResultHandler(resultHandler, values);
}
源码核心逻辑拆解:
- 通过rowNum计数器遍历ResultSet,逐行比对偏移量
- rowNum小于offset时,直接跳过当前行,不做数据封装
- 达到offset后,开始封装数据,直到收集满limit条后停止遍历
- 全程不修改SQL,仅对内存中的结果集做裁剪,无数据库分页交互
五、核心特性与局限性
5.1 核心优势
- 零侵入:无需修改SQL、无需配置插件,兼容所有数据库
- 轻量简洁:仅依赖MyBatis原生API,无第三方依赖
- 适配简单场景:小数据量查询下,分页逻辑简洁高效
5.2 致命局限性
- 性能极差:大数据量/大表查询时,数据库需返回全量数据,网络传输耗时、内存占用飙升,极易触发OOM
- 资源浪费:数据库执行全量查询、应用加载全量结果集,CPU和内存资源无效消耗
- 无总条数:默认不返回总记录数,无法实现常规分页的「总页数、页码」计算
六、适用场景与替代方案
6.1 适用场景
仅适用于数据量极小的场景(如字典表、配置表、缓存后数据),这类场景全量查询耗时可忽略,能发挥其轻量便捷的优势。
6.2 推荐替代方案
- 物理分页:手写SQL LIMIT/OFFSET(MySQL)、ROWNUM(Oracle)
- 分页插件:PageHelper、MyBatis-Plus分页插件,底层改写SQL实现数据库物理分页,性能远超RowBounds
ps:如果这篇帖子对于还在找工作和找实习的你有所帮助,可以关注我,给本贴点赞、评论、收藏并订阅专栏;同时不要吝啬您的花花
本专栏聚焦Java主流持久层框架MyBatis,从基础搭建到源码原理,系统拆解核心组件、动态SQL、结果映射与缓存机制。助力开发者从入门到精通,掌握高效数据层开发技能,适配电商、金融等复杂业务场景。


