✅ 一、核心概念

概念 说明
分页插件 通过拦截器机制自动在 SQL 中追加 LIMITOFFSET 实现分页
Page 对象 MyBatis-Plus 提供的分页参数封装类,包含 currentsizetotal 等字段
PaginationInnerInterceptor 新版(3.5+)分页插件核心类,替代旧版 PaginationInterceptor
DbType 指定数据库类型(如 MYSQL、POSTGRE_SQL),用于 SQL 方言适配

✅ 二、操作步骤(详细)

✅ 步骤1:添加依赖(pom.xml)

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-extension</artifactId>
    <version>3.5.9</version>
</dependency>
<dependency>
    <groupId>com.github.jsqlparser</groupId>
    <artifactId>jsqlparser</artifactId>
    <version>4.6</version>
</dependency>

注意:3.5+ 版本必须引入 jsqlparser,否则分页插件无法生效。


✅ 步骤2:配置分页插件

@Configuration
public class MyBatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}

说明:如果你使用的是 PostgreSQL、Oracle 等,请替换 DbType


✅ 步骤3:Mapper 接口继承 BaseMapper

@Mapper
public interface UserMapper extends BaseMapper<User> {
    // 无需手写分页 SQL
}

✅ 步骤4:分页查询代码示例

Page<User> page = new Page<>(1, 10); // 第一页,每页10条
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.like("name", "张");

IPage<User> result = userMapper.selectPage(page, wrapper);

System.out.println("总条数:" + result.getTotal());
System.out.println("当前页数据:" + result.getRecords());

⚠️ 三、常见错误与解决方案

错误描述 原因 解决方案
分页无效/返回全部数据 插件未注册或版本错误 检查 MybatisPlusInterceptor 是否正确配置,版本是否 ≥ 3.5
找不到 PaginationInnerInterceptor 缺少 jsqlparser 依赖 添加 jsqlparser 依赖
多表分页结果错误 分页作用于主表,但 SQL 是多表联查 使用子查询或先分页主表再关联

🎯 四、注意事项

  • Page 对象必须作为第一个参数传入分页方法;
  • 分页插件不支持复杂 SQL(如嵌套子查询、UNION),建议用自定义 SQL 或手动分页;
  • 排序字段建议使用唯一字段(如主键)避免分页结果抖动;
  • 分页参数建议从前端校验(如最大页码、每页最大条数限制)。

✅ 五、使用技巧与最佳实践

场景 技巧
大数据分页 使用“先查主键再查详情”策略,避免深分页扫描
多表分页 先分页主表,再批量查询子表数据,避免 JOIN 后分页不准确
自定义最大分页条数 自定义 Page 类限制 size 最大值
SQL 日志调试 开启 SQL 日志,确认分页 SQL 是否正确拼接
# application.yml
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

✅ 六、性能优化建议

优化点 实施建议
深分页 使用主键索引+子查询替代 OFFSET
排序字段 联合排序时加上主键,避免重复值导致分页错乱
缓存 热点分页数据使用 Redis 缓存
只查必要字段 使用 select() 指定字段,减少 IO

✅ 七、完整实战示例(电商订单分页)

public Page<OrderVO> getOrderPage(int current, int size, String status) {
    Page<Order> page = new Page<>(current, size);
    QueryWrapper<Order> wrapper = new QueryWrapper<>();
    wrapper.eq("status", status).orderByDesc("id");

    IPage<Order> orderPage = orderMapper.selectPage(page, wrapper);

    List<Long> userIds = orderPage.getRecords().stream()
        .map(Order::getUserId).collect(Collectors.toList());

    List<User> users = userMapper.selectBatchIds(userIds);

    List<OrderVO> voList = orderPage.getRecords().stream().map(order -> {
        OrderVO vo = new OrderVO();
        BeanUtils.copyProperties(order, vo);
        User user = users.stream()
            .filter(u -> u.getId().equals(order.getUserId()))
            .findFirst().orElse(null);
        vo.setUserName(user != null ? user.getName() : null);
        return vo;
    }).collect(Collectors.toList());

    Page<OrderVO> result = new Page<>();
    result.setRecords(voList);
    result.setTotal(orderPage.getTotal());
    return result;
}

✅ 八、总结一句话

配置分页插件 ≠ 万事大吉,合理分页策略 + SQL优化 + 业务限制,才是高性能分页的终极解法。