MyBatis 分页
列表页永远要分页。分页有两种:逻辑分页先把符合条件的数据全部查出再截断(数据多了就崩);物理分页直接生成带 LIMIT/OFFSET 的 SQL,只查需要的行。MyBatis 核心自带的能力只有前者(RowBounds),生产环境的分页几乎都交给 PageHelper 这类插件做物理分页。
RowBounds:内置但够用吗
RowBounds 通过偏移量 + 条数描述"要哪一段",两种用法:
// 用法一:SqlSession API 传入
List<User> list = session.selectList("...UserMapper.selectAll",
null, new RowBounds(0, 10));
// 用法二:Mapper 接口方法声明 RowBounds 参数
List<User> page(User user, RowBounds rowBounds);
<select id="selectAll" resultType="user">
SELECT id, name, age FROM user
</select>
局限:MyBatis 核心不生成数据库方言的分页 SQL(如 MySQL 的 LIMIT),RowBounds 只是在结果层面做偏移截断——数据照查、照传,只是丢弃用不到的行(逻辑分页)。数据量大时性能差,这也是"RowBounds 只适合小数据量"的原因(细节以官方文档为准)。
PageHelper:物理分页主流方案
PageHelper 是第三方分页插件(官方仓库 mybatis-pagehelper),原理是第 12 章的 Executor 拦截器:拦截查询,自动在 SQL 后面拼上对应数据库的分页方言。
第一步:引入依赖
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.x</version>
</dependency>
第二步:注册拦截器(5.x 用 PageInterceptor,更早版本直接写 PageHelper 类名)
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<property name="helperDialect" value="mysql"/>
<property name="reasonable" value="true"/>
</plugin>
</plugins>
helperDialect 指定数据库(mysql/oracle/…);reasonable=true 时页码越界会自动修正到首/末页。
第三步:编码
// 1. 分页参数:只对紧随其后的第一条查询生效
PageHelper.startPage(1, 10);
List<User> list = userMapper.selectAll();
// 2. 返回的 List 实际是 Page,能直接拿到总数
long total = ((Page<User>) list).getTotal();
// 3. 更常用:包一层 PageInfo,页面需要的字段都齐了
PageInfo<User> pageInfo = new PageInfo<>(list);
System.out.println(pageInfo.getTotal()); // 总条数
System.out.println(pageInfo.getPages()); // 总页数
System.out.println(pageInfo.getList()); // 当前页数据
startPage 内部使用 ThreadLocal:调用后紧跟着执行的那一次 Mapper 查询会被分页,查询结束后参数自动清除。中途不要再发别的查询,也不要跨方法使用,否则容易分页错乱或漏分页。
Spring Boot 场景:可用 pagehelper-spring-boot-starter,在 application.yml 配置 pagehelper.helper-dialect、pagehelper.reasonable 等(属性名与版本以官方文档为准)。
常见坑汇总
- RowBounds 是逻辑分页,只适合数据量很小的场景。
- startPage 与查询之间不能插入其他查询/逻辑。
- count 查询会自动生成并执行,大表统计慢时可关注其 count SQL 的效率。
- 分页插件会把"分页上下文"绑定到当前线程,线程池/异步场景要小心参数残留(按官方建议使用)。
小结
内置 RowBounds 只做结果截断,扛不住大数据量;物理分页用 PageHelper:注册 PageInterceptor → startPage 后紧跟查询 → 用 Page/PageInfo 取数。插件版本、数据库方言配置一律以官方文档为准。