MyBatis 分页查询
·
MyBatis 分页查询
一句话总结
MyBatis 分页主要有三种典型实现:SQL 手写物理分页、RowBounds 逻辑分页、分页插件(如 PageHelper / MyBatis-Plus)物理分页。实践中推荐使用“物理分页 + 分页插件”,避免一次查全表导致内存压力和性能问题。
一、原生 MyBatis 分页(手动 LIMIT / OFFSET)
通过在 SQL 中手动编写 LIMIT / OFFSET(或各数据库方言,比如 Oracle 的 ROWNUM)来实现物理分页。
1. 实体类定义
public class PageParam {
private int pageNum; // 第几页,从 1 开始
private int pageSize; // 每页条数
// getter / setter 省略
}
2. Mapper XML
<select id="selectUsersByPage" resultType="User">
SELECT *
FROM user
LIMIT #{pageSize} OFFSET #{offset}
</select>
3. Mapper 接口
public interface UserMapper {
List<User> selectUsersByPage(@Param("pageSize") int pageSize,
@Param("offset") int offset);
}
4. 调用代码
int pageNum = 2;
int pageSize = 10;
int offset = (pageNum - 1) * pageSize;
List<User> users = userMapper.selectUsersByPage(pageSize, offset);
5. 特点
- 优点:
- SQL 直观、无额外依赖;
- 完全掌控 SQL,便于做复杂优化。
- 缺点:
- 需要手动计算偏移量;
- 不同数据库 SQL 语法不同,需要维护多套方言;
- 容易忽略统计总数、排序等细节。
二、使用 RowBounds(逻辑分页 / 内存分页)
利用 MyBatis 内置的 RowBounds 对象实现分页,它是先执行完整 SQL 查询,再在内存中截取一部分记录。
1. Mapper 接口
public interface UserMapper {
List<User> selectAllUsers(RowBounds rowBounds);
}
2. Mapper XML
<select id="selectAllUsers" resultType="User">
SELECT * FROM user
</select>
3. 调用代码
int pageNum = 2;
int pageSize = 10;
RowBounds rowBounds =
new RowBounds((pageNum - 1) * pageSize, pageSize);
List<User> users = userMapper.selectAllUsers(rowBounds);
4. 特点
- 优点:
- 使用方式统一、API 简单;
- 对 SQL 无侵入,XML 不需要写分页语句。
- 缺点(面试高频点):
- 默认实现是内存分页——数据库查出所有数据后在 JVM 内存中做截断;
- 大数据量时严重影响性能,甚至 OOM;
- 因为是逻辑分页,不是真正的物理分页,一般不推荐在生产大表场景使用。
很多面试题会问:RowBounds 是逻辑分页还是物理分页?为什么在大数据量场景不推荐?
标准回答:逻辑分页,先查全表再在内存截取,会产生严重的内存与性能问题。
三、使用分页插件(推荐:PageHelper)
通过分页插件(PageHelper 等)在 MyBatis 拦截层自动改写 SQL,添加 LIMIT / OFFSET 和统计总数的 SQL,实现真正的物理分页。
1. Maven 依赖
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.3.2</version>
</dependency>
2. MyBatis 配置插件(XML 示例)
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<!-- 指定数据库方言 -->
<property name="helperDialect" value="mysql"/>
</plugin>
</plugins>
Spring Boot 场景下一般使用 Starter,配置会略有不同,思想一致。
3. Mapper XML
<select id="selectAllUsers" resultType="User">
SELECT * FROM user
</select>
4. 调用代码
int pageNum = 2;
int pageSize = 10;
// 开启分页(只对紧跟着的第一个查询生效)
PageHelper.startPage(pageNum, pageSize);
List<User> users = userMapper.selectAllUsers();
// 包装成 PageInfo,包含分页元数据
PageInfo<User> pageInfo = new PageInfo<>(users);
5. 常用分页信息
long total = pageInfo.getTotal(); // 总记录数
int pages = pageInfo.getPages(); // 总页数
int curr = pageInfo.getPageNum(); // 当前页
List<User> list = pageInfo.getList(); // 当前页数据
6. 特点
- 优点:
- 真正的物理分页,只查当前页数据;
- 自动生成分页 SQL,支持多数据库方言;
- 封装了分页结果,开发效率高。
- 缺点:
- 需要额外引入第三方库;
- 底层改写 SQL,有时排查问题需要理解插件工作原理。
四、MyBatis-Plus 分页(集成方案)
若项目基于 MyBatis-Plus,可以使用其内置分页插件,API 更加统一、符合 MP 风格。
1. Maven 依赖
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3.1</version>
</dependency>
2. 配置分页插件(Spring Boot)
@Configuration
public class MyBatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(
new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
3. Mapper 接口
public interface UserMapper extends BaseMapper<User> {
}
4. 调用代码
int pageNum = 2;
int pageSize = 10;
Page<User> page = new Page<>(pageNum, pageSize);
IPage<User> userPage = userMapper.selectPage(page, null);
List<User> users = userPage.getRecords();
long total = userPage.getTotal();
5. 特点
- 优点:
- 与 MyBatis-Plus 深度集成,支持 Lambda 条件构造器;
- 分页 + 条件查询写法统一,代码简洁。
- 缺点:
- 需要整体引入 MyBatis-Plus,对老项目迁移成本较高。
五、分页方式对比与面试回答思路
| 方式 | 分页类型 | 性能 | 复杂度 | 适用场景 | 依赖 |
|---|---|---|---|---|---|
| 原生手动分页 | 物理分页 | 高 | 较高 | 需要精细控制 SQL / 优化场景 | 无 |
| RowBounds | 逻辑分页 | 低 | 低 | 小数据量、简单快速分页 | 无 |
| PageHelper | 物理分页 | 高 | 中 | 传统 MyBatis 项目推荐 | PageHelper |
| MyBatis-Plus 分页插件 | 物理分页 | 高 | 低 | 已使用 MP 的 Spring Boot 项目 | MyBatis-Plus |
面试常见追问点(可以提前准备的回答)
-
1)问:MyBatis 分页有哪几种方式?推荐哪种?为什么?
- 回答框架:
RowBounds逻辑分页(内存截断,适合小数据);- SQL 手写
LIMIT/OFFSET等物理分页; - 分页插件(PageHelper / MyBatis-Plus)自动生成物理分页 SQL;
- 生产一般推荐物理分页 + 插件,既保证性能又简化开发。
- 回答框架:
-
2)问:RowBounds 有什么问题?
- 逻辑分页,默认先查全部,再在内存分页;大数据量会拖垮内存与性能,所以不建议对大表使用。
-
3)问:分页时常见的注意点有哪些?
- 必须有稳定的
ORDER BY,否则翻页数据可能不稳定; - 大偏移量分页(
LIMIT 100000,10)性能差,可以用“基于主键 / 游标”的方式优化; - 统计总数时要注意
COUNT与复杂JOIN的性能,可以拆分查询或走汇总表。
- 必须有稳定的
更多推荐



所有评论(0)