Skip to content

MyBatis-Plus 分页插件

  MyBatis-Plus 内置物理分页插件,一行配置即可实现分页查询,支持多表关联查询分页。

一、分页插件配置

java
@Configuration
public class MybatisPlusConfig {

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();

        // 添加分页插件
        PaginationInnerInterceptor pagination = new PaginationInnerInterceptor();
        // 数据库类型(MySQL)
        pagination.setDbType(DbType.MYSQL);
        // 单页最大条数限制(防止恶意查询)
        pagination.setMaxLimit(500L);
        // 溢出处理(超过总页数后默认回到首页)
        pagination.setOverflow(true);

        interceptor.addInnerInterceptor(pagination);
        return interceptor;
    }
}

二、基础分页

2.1 BaseMapper 分页

java
// 创建分页对象
Page<User> page = new Page<>(1, 10);  // 第 1 页,每页 10 条

// 无条件分页
Page<User> result = userMapper.selectPage(page, null);
System.out.println("总条数:" + result.getTotal());
System.out.println("总页数:" + result.getPages());
System.out.println("当前页:" + result.getCurrent());
System.out.println("数据:" + result.getRecords());

// 带条件分页
Page<User> result = userMapper.selectPage(
    new Page<>(1, 10),
    new LambdaQueryWrapper<User>()
        .gt(User::getAge, 20)
        .orderByDesc(User::getCreateTime)
);

2.2 Service 层分页

java
// 方式一:Page 对象
Page<User> page = new Page<>(1, 10);
userService.page(page);
List<User> records = page.getRecords();

// 方式二:直接返回分页结果
IPage<User> result = userService.page(
    new Page<>(1, 10),
    new LambdaQueryWrapper<User>().gt(User::getAge, 20)
);

// 方式三:链式分页
IPage<User> result = userService.lambdaQuery()
    .gt(User::getAge, 20)
    .page(new Page<>(1, 10));

三、自定义多表分页

3.1 Mapper 接口

java
@Mapper
public interface UserMapper extends BaseMapper<User> {

    // 自定义分页查询(多表关联)
    IPage<UserVO> selectUserPage(Page<UserVO> page,
                                  @Param("query") UserQuery query);
}

3.2 Mapper XML

xml
<select id="selectUserPage" resultType="com.example.vo.UserVO">
    SELECT u.id, u.name, u.age, o.order_no, o.amount
    FROM user u
    LEFT JOIN t_order o ON u.id = o.user_id
    <where>
        <if test="query.name != null and query.name != ''">
            AND u.name LIKE CONCAT('%', #{query.name}, '%')
        </if>
        <if test="query.minAge != null">
            AND u.age &gt;= #{query.minAge}
        </if>
    </where>
    ORDER BY u.create_time DESC
</select>
java
// 调用
Page<UserVO> page = new Page<>(1, 10);
UserQuery query = new UserQuery();
query.setName("张");
IPage<UserVO> result = userMapper.selectUserPage(page, query);

四、PageInfo 封装

java
// 分页查询
Page<User> page = new Page<>(1, 10);
userMapper.selectPage(page, null);

// 封装为 PageInfo(兼容 PageHelper 风格)
PageInfo<User> pageInfo = new PageInfo<>(page.getRecords());
// 或
PageInfo<User> pageInfo = PageInfo.of(page);

System.out.println("当前页:" + pageInfo.getPageNum());
System.out.println("页大小:" + pageInfo.getPageSize());
System.out.println("总条数:" + pageInfo.getTotal());
System.out.println("总页数:" + pageInfo.getPages());
System.out.println("是否有上一页:" + pageInfo.isHasPreviousPage());
System.out.println("是否有下一页:" + pageInfo.isHasNextPage());

五、分页参数说明

java
// Page 构造方法
Page<T> page = new Page<>();
page.setCurrent(1);       // 当前页(默认 1)
page.setSize(10);         // 每页条数(默认 10)
page.setMaxLimit(500L);   // 单页最大条数限制
page.setOptimizeCountSql(true);  // 是否优化 COUNT SQL(默认 true)
page.setSearchCount(true);       // 是否查询总数(默认 true)

六、分页插件原理

MyBatis-Plus 分页原理:

  ① PaginationInnerInterceptor 拦截 Executor.query()
     → 检测到 Page 参数,进入分页逻辑

  ② 执行 COUNT 查询
     → 包装原始 SQL:SELECT COUNT(*) FROM (原始SQL) tmp
     → 获取总记录数

  ③ 计算分页参数
     → 总页数 = 总记录数 / 每页条数
     → 溢出处理:页码超出总页数时回到首页

  ④ 拼接分页 SQL
     → MySQL:原始 SQL + LIMIT offset, size
     → Oracle:嵌套查询 + ROWNUM
     → PostgreSQL:原始 SQL + LIMIT size OFFSET offset

  ⑤ 执行分页 SQL 返回结果

七、速记总结

分页三步走:
  ① 配置分页插件(MybatisPlusInterceptor + PaginationInnerInterceptor)
  ② 创建 Page 对象(new Page<>(页码, 条数))
  ③ 调用 selectPage 或 page 方法

速记口诀:
  插件配置一行起,Page 对象传页码,
  selectPage 或 page 方法,自动拼 LIMIT 查总数。

八、面试要点

问题答案要点
分页怎么配置?MybatisPlusInterceptor + PaginationInnerInterceptor
自定义多表分页?Mapper 方法第一个参数传 Page 对象
分页原理?拦截 Executor.query() → COUNT 查总数 → 拼接 LIMIT
和 PageHelper 区别?MP 内置分页,更原生,不需要 ThreadLocal