Prompts-MCP 686 skills · ai / design / design-pattern / framework / fundamentals / habit / lang / tech-selection /mcp/sse
framework framework/mybatis/pagination.md medium

mybatis-pagination

MyBatis 物理分页 — PageHelper.startPage 或 MyBatis-Plus IPage 下推 limit,禁 RowBounds 全查内存分页,count 查询单独优化。Use when 写分页列表接口 / 排查分页慢或 OOM / 评审 RowBounds 用法时。

物理分页pagehelperipagerowbounds内存分页count 优化
paths
  • *.java
  • *.xml

MyBatis · 分页

本条只管「分页怎么做对」。关联数据导致的循环查见 n-plus-one.md

规则

事项 约定
分页方式 物理分页:PageHelper 拦截器 或 MyBatis-Plus IPage,把 limit/offset 下推到 SQL
禁用 RowBounds(先全查再内存截断,大表 OOM)、subList 手动截断
startPage 位置 PageHelper 必须紧贴要分页的那条查询,中间不夹其它查询
count 框架自动生成 count;count 慢时手写 countXxx,去掉 join / order by
深分页 offset 很大时改用「游标 / 上次最大 id」键集分页

正例

// ✅ PageHelper:startPage 紧贴下一条查询
PageHelper.startPage(pageNum, pageSize);
List<User> list = userMapper.listByStatus(status);   // 自动 limit
PageInfo<User> page = new PageInfo<>(list);           // total / pages 自动算
// ✅ MyBatis-Plus IPage:分页对象下推
IPage<User> page = new Page<>(pageNum, pageSize);
userMapper.selectPage(page, new LambdaQueryWrapper<User>().eq(User::getStatus, status));

反例

// ❌ RowBounds:MyBatis 把全表查进内存再截断 —— 百万行直接 OOM
userMapper.list(new RowBounds(offset, limit));

// ❌ 全查后 subList:同样把全量拉进内存
List<User> all = userMapper.listAll();
return all.subList(offset, offset + size);

// ❌ startPage 后先查了别的,分页作用到错的查询上
PageHelper.startPage(p, s);
userMapper.countSomething();   // 分页被它消费掉了
List<User> list = userMapper.list();

理由:RowBounds / subList 是逻辑分页,数据量一大就拉爆内存;PageHelper 的 startPage 只对其后第一条查询生效,插队的查询会把分页吃掉。

自检

  • [ ] 用 PageHelper 或 IPage 物理分页,没用 RowBounds / subList?
  • [ ] startPage 紧贴目标查询,中间无其它查询?
  • [ ] count 慢时单独写了去 join / 去 order by 的 count?
  • [ ] 深分页场景评估了键集分页?

相关