MyBatis 分页机制详解:从 RowBounds 到物理分页实践

简介: MyBatis分页策略解析:逻辑分页(RowBounds)将全量数据加载至内存,仅适用于小数据量;物理分页通过SQL层面限制返回数据,性能更优。推荐使用PageHelper插件,自动适配数据库方言,一行代码实现高效分页,避免OOM风险,提升系统稳定性。

在 Web 开发中,分页是处理大量数据展示的必备功能。MyBatis 本身不强制提供分页实现,但支持多种分页策略,主要分为 逻辑分页(内存分页)物理分页(数据库分页)。理解它们的原理与适用场景,对系统性能和稳定性至关重要。


一、逻辑分页:MyBatis 自带的 RowBounds

RowBounds 是 MyBatis 提供的内置分页工具,属于逻辑分页——即先查询出全部结果集,再在 Java 内存中截取指定范围的数据。

使用示例:
// Mapper 接口(无需特殊定义)
List<User> selectAllUsers();
// Service 层调用
int offset = (pageNum - 1) * pageSize; // 起始行
int limit = pageSize;                  // 获取条数
RowBounds rowBounds = new RowBounds(offset, limit);
List<User> users = userMapper.selectAllUsers(rowBounds);
底层原理:
  • 执行原始 SQL(如 SELECT * FROM user),获取完整 ResultSet
  • MyBatis 遍历结果集,跳过 offset 条,再取 limit 条;
  • 未生成带 LIMIT 的 SQL,数据库仍返回全部数据。

⚠️ 缺点

  • 内存开销大:若表有 100 万条数据,即使只取第 1 页,也会加载全部到内存;
  • 性能差:大数据量下响应慢,甚至引发 OutOfMemoryError
  • 网络传输浪费:大量无用数据在网络中传输。

适用场景

仅适用于数据量极小(如配置表、字典表)且分页需求简单的场景。


二、物理分页:真正的高效分页

物理分页通过在 SQL 层面添加分页语句(如 LIMITROWNUM),让数据库只返回所需数据,大幅减少 I/O、内存和网络开销。

常见的物理分页实现方式包括:

1. 手写 SQL 分页

直接在 Mapper XML 中编写带分页关键字的 SQL(需适配不同数据库):

<!-- MySQL -->
<select id="selectUsersByPage" resultType="User">
  SELECT * FROM user
  LIMIT #{offset}, #{limit}
</select>
<!-- Oracle -->
<select id="selectUsersByPage" resultType="User">
  SELECT * FROM (
    SELECT ROWNUM rn, t.* FROM (
      SELECT * FROM user ORDER BY id
    ) t WHERE ROWNUM &lt;= #{endRow}
  ) WHERE rn > #{startRow}
</select>

✅ 优点:完全可控;

❌ 缺点:需为不同数据库维护不同 SQL,维护成本高。


2. 第三方插件:PageHelper(推荐)

PageHelper 是最流行的 MyBatis 分页插件,自动识别数据库类型并重写 SQL。

使用步骤:
  1. 引入依赖(Maven):
<dependency>
  <groupId>com.github.pagehelper</groupId>
  <artifactId>pagehelper-spring-boot-starter</artifactId>
  <version>1.4.6</version>
</dependency>
  1. 在查询前开启分页:
PageHelper.startPage(pageNum, pageSize);
List<User> users = userMapper.selectAllUsers(); // 原始查询方法,无需改写
  1. 获取分页信息(可选):
PageInfo<User> pageInfo = new PageInfo<>(users);
long total = pageInfo.getTotal(); // 总记录数
插件原理:
  • 拦截 SQL 执行;
  • 自动拼接分页语句(MySQL → LIMIT,Oracle → ROWNUM,SQL Server → OFFSET FETCH 等);
  • 额外执行一条 COUNT(*) 查询总条数

优势

  • 一行代码实现分页;
  • 自动适配主流数据库;
  • 返回完整分页元数据(总页数、是否首页等)。

3. 数组分页(伪物理分页)

在 DAO 层查出全部数据,Service 层用 List.subList() 截取:

public List<Student> queryStudentsByArray(int currPage, int pageSize) {
    List<Student> all = studentMapper.queryAll();
    int start = (currPage - 1) * pageSize;
    int end = Math.min(start + pageSize, all.size());
    return all.subList(start, end);
}

⚠️ 本质仍是逻辑分页!只是手动实现了 RowBounds 的功能,同样存在内存溢出风险,不推荐用于生产环境。


4. 自定义拦截器分页

通过实现 MyBatis Interceptor 接口,拦截特定命名的查询(如 *ByPage),动态拼接 LIMIT

✅ 适合有统一规范的团队;

❌ 开发成本较高,需处理 SQL 解析、方言适配等细节。


三、逻辑分页 vs 物理分页:对比总结

对比项 逻辑分页(RowBounds) 物理分页(PageHelper / 手写 SQL)
数据加载 全量加载到内存 数据库只返回当前页
内存占用 高(随总数据量增长) 低(仅当前页)
网络传输 大量无用数据 仅有效数据
性能(大数据) 极差,可能 OOM 优秀
数据库压力 高(全表扫描) 低(利用索引+分页)
适用场景 小表、配置类数据 所有业务主表

📌 核心原则

物理分页总是优先于逻辑分页。除非数据量极小(< 1000 条),否则应避免使用 RowBoundssubList 分页。


通过合理选择分页策略,尤其是采用 PageHelper 等物理分页插件,你可以在保证代码简洁的同时,获得高性能、高稳定性的分页能力,为应用打下坚实的数据访问基础。


相关文章
|
12天前
|
数据采集 人工智能 安全
|
7天前
|
机器学习/深度学习 人工智能 前端开发
构建AI智能体:七十、小树成林,聚沙成塔:随机森林与大模型的协同进化
随机森林是一种基于决策树的集成学习算法,通过构建多棵决策树并结合它们的预测结果来提高准确性和稳定性。其核心思想包括两个随机性:Bootstrap采样(每棵树使用不同的训练子集)和特征随机选择(每棵树分裂时只考虑部分特征)。这种方法能有效处理大规模高维数据,避免过拟合,并评估特征重要性。随机森林的超参数如树的数量、最大深度等可通过网格搜索优化。该算法兼具强大预测能力和工程化优势,是机器学习中的常用基础模型。
344 164
|
6天前
|
机器学习/深度学习 自然语言处理 机器人
阿里云百炼大模型赋能|打造企业级电话智能体与智能呼叫中心完整方案
畅信达基于阿里云百炼大模型推出MVB2000V5智能呼叫中心方案,融合LLM与MRCP+WebSocket技术,实现语音识别率超95%、低延迟交互。通过电话智能体与座席助手协同,自动化处理80%咨询,降本增效显著,适配金融、电商、医疗等多行业场景。
345 155
|
7天前
|
编解码 人工智能 自然语言处理
⚽阿里云百炼通义万相 2.6 视频生成玩法手册
通义万相Wan 2.6是全球首个支持角色扮演的AI视频生成模型,可基于参考视频形象与音色生成多角色合拍、多镜头叙事的15秒长视频,实现声画同步、智能分镜,适用于影视创作、营销展示等场景。
581 4
|
15天前
|
SQL 自然语言处理 调度
Agent Skills 的一次工程实践
**本文采用 Agent Skills 实现整体智能体**,开发框架采用 AgentScope,模型使用 **qwen3-max**。Agent Skills 是 Anthropic 新推出的一种有别于mcp server的一种开发方式,用于为 AI **引入可共享的专业技能**。经验封装到**可发现、可复用的能力单元**中,每个技能以文件夹形式存在,包含特定任务的指导性说明(SKILL.md 文件)、脚本代码和资源等 。大模型可以根据需要动态加载这些技能,从而扩展自身的功能。目前不少国内外的一些框架也开始支持此种的开发方式,详细介绍如下。
1018 7

热门文章

最新文章