✅ 一、核心概念
概念 |
说明 |
分页插件 |
通过拦截器机制自动在 SQL 中追加 LIMIT 、OFFSET 实现分页 |
Page 对象 |
MyBatis-Plus 提供的分页参数封装类,包含 current 、size 、total 等字段 |
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优化 + 业务限制,才是高性能分页的终极解法。