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 的性能,可以拆分查询或走汇总表。
Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐