mybatis 分页

940 阅读3分钟

在 MyBatis 中,实现分页主要有以下几种常见方式:

  1. 使用 LIMIT + OFFSET 分页(适用于 MySQL、PostgreSQL)
  2. 使用 RowBounds 分页(MyBatis 自带)
  3. 使用分页插件(如 PageHelper
  4. 通过存储过程或其他数据库特性分页(如 Oracle 的 ROWNUMROW_NUMBER()

1. 使用 LIMIT + OFFSET 方式(推荐)

  • LIMITOFFSET 是 MySQL、PostgreSQL 等数据库提供的标准分页方式。
  • LIMIT 表示返回多少条记录。
  • OFFSET 表示跳过多少条记录。

📝 示例 1:使用 LIMIT + OFFSET 直接分页

<select id="getUsersByPage" resultType="User">
    SELECT * FROM user
    ORDER BY id
    LIMIT #{pageSize} OFFSET #{offset}
</select>

Java 代码:

public List<User> getUsersByPage(int pageNum, int pageSize) {
    int offset = (pageNum - 1) * pageSize;
    return userMapper.getUsersByPage(pageSize, offset);
}

完整 Mapper 接口:

@Mapper
public interface UserMapper {

    @Select("SELECT * FROM user ORDER BY id LIMIT #{pageSize} OFFSET #{offset}")
    List<User> getUsersByPage(@Param("pageSize") int pageSize, @Param("offset") int offset);
}

生成的 SQL 语句:

  • 查询第 2 页,每页 10 条:
SELECT * FROM user ORDER BY id LIMIT 10 OFFSET 10;

解释:
LIMIT 10 → 返回 10 条记录
OFFSET 10 → 跳过前 10 条记录


2. 使用 RowBounds 方式(MyBatis 自带)

  • RowBounds 是 MyBatis 内置的分页对象。
  • MyBatis 在查询结果返回后再进行分页。
  • 由于是在内存中分页,不推荐在数据量很大时使用

📝 示例 2:使用 RowBounds 分页

<select id="getUsers" resultType="User">
    SELECT * FROM user ORDER BY id
</select>

Java 代码:

public List<User> getUsersByRowBounds(int pageNum, int pageSize) {
    int offset = (pageNum - 1) * pageSize;
    RowBounds rowBounds = new RowBounds(offset, pageSize);
    return userMapper.getUsersByRowBounds(rowBounds);
}

完整 Mapper 接口:

@Mapper
public interface UserMapper {

    @Select("SELECT * FROM user ORDER BY id")
    List<User> getUsersByRowBounds(RowBounds rowBounds);
}

生成的 SQL 语句:

  • MyBatis 会生成完整的 SQL 语句并在内存中分页
SELECT * FROM user ORDER BY id;

解释:
RowBounds 在 MyBatis 层进行分页,效率低
✅ 适合小数据量


3. 使用分页插件(推荐)

  • 第三方分页插件(如 PageHelper)是最常用的分页方式。
  • PageHelper 在执行 SQL 前自动修改 SQL 语句,直接在数据库中进行分页,性能高。
  • 兼容 MySQL、PostgreSQL、Oracle、SQL Server 等常见数据库。

引入 Maven 依赖:

<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>5.3.2</version>
</dependency>

📝 示例 3:使用 PageHelper 进行分页

<select id="getUsersByPage" resultType="User">
    SELECT * FROM user ORDER BY id
</select>

Java 代码:

public List<User> getUsersByPage(int pageNum, int pageSize) {
    PageHelper.startPage(pageNum, pageSize);
    return userMapper.getUsersByPage();
}

完整 Mapper 接口:

@Mapper
public interface UserMapper {

    @Select("SELECT * FROM user ORDER BY id")
    List<User> getUsersByPage();
}

生成的 SQL 语句:

SELECT * FROM user ORDER BY id LIMIT 10 OFFSET 10;

PageHelper 自动生成的分页查询:

PageHelper.startPage(pageNum, pageSize) 会在执行 SQL 之前,自动修改 SQL 语句
✅ 在 SQL 级别分页,性能高


引入 PageHelper 配置(MyBatis 配置):

mybatis.configuration.default-fetch-size=100
mybatis.configuration.default-statement-timeout=30

结果封装为 PageInfo(可分页返回):

PageInfo<User> pageInfo = new PageInfo<>(userMapper.getUsersByPage());
System.out.println("总页数:" + pageInfo.getPages());
System.out.println("当前页:" + pageInfo.getPageNum());
System.out.println("总记录数:" + pageInfo.getTotal());
System.out.println("结果集:" + pageInfo.getList());

4. 使用 Oracle 的 ROWNUM 方式分页

Oracle 中不支持 LIMITOFFSET,需要通过 ROWNUMROW_NUMBER() 进行分页。


📝 示例 4:使用 Oracle 的 ROWNUM 进行分页

<select id="getUsersByPage" resultType="User">
    SELECT *
    FROM (
        SELECT a.*, ROWNUM rnum
        FROM (
            SELECT * FROM user ORDER BY id
        ) a
        WHERE ROWNUM <= #{offset} + #{pageSize}
    )
    WHERE rnum > #{offset}
</select>

Java 代码:

public List<User> getUsersByPage(int pageNum, int pageSize) {
    int offset = (pageNum - 1) * pageSize;
    return userMapper.getUsersByPage(offset, pageSize);
}

生成的 SQL 语句:

SELECT *
FROM (
    SELECT a.*, ROWNUM rnum
    FROM (
        SELECT * FROM user ORDER BY id
    ) a
    WHERE ROWNUM <= 20
)
WHERE rnum > 10;

🚀 5. 性能对比

方式适用场景优势劣势
LIMIT + OFFSETMySQL、PostgreSQL性能好,数据库级别分页大数据时性能下降
RowBounds小数据量直接在内存中分页数据量大时内存占用高
PageHelper通用直接生成分页 SQL,性能好需引入外部库
ROWNUMOracle适合 Oracle语法复杂,兼容性差

🎯 最佳实践

✅ 小数据量 → RowBounds
✅ 中等数据量 → LIMIT + OFFSET
✅ 大数据量 → PageHelper
✅ Oracle → ROWNUM or ROW_NUMBER()


🎉 总结:

  1. 小数据量 → 用 RowBounds 处理
  2. 中等数据量 → 用 LIMIT + OFFSET
  3. 大数据量 → 用 PageHelper 插件
  4. Oracle → 用 ROWNUMROW_NUMBER()

👉 推荐使用 PageHelper,性能和兼容性更好! 😎