大家好,我是“不想打工的码农”。

最近在带几个刚入行的朋友做毕业设计,发现一个极其普遍的问题

“明明用了 MyBatis Plus,为什么 IPage 返回的 total 是 0?数据能查出来,但总条数和总页数都是错的!”

一问才知道,他们漏掉了最关键的一步——注册分页插件!

今天我就手把手教你怎么用 MyBatis Plus 自带的分页功能,彻底告别手写 limit offset, size 和 count(*) 的原始时代。


一、为什么 MyBatis Plus 分页这么香?

传统分页你需要:

  1. 写 SQL 查总条数:SELECT COUNT(*) FROM student WHERE ...
  2. 再写 SQL 查当前页:SELECT * FROM student WHERE ... LIMIT 10 OFFSET 20
  3. 手动计算总页数、是否最后一页……

而 MyBatis Plus 只需:

  • 调用 selectPage(page, queryWrapper)
  • 自动返回:当前页数据 + 总记录数 + 总页数 + 当前页码 + 每页大小

前端直接拿去渲染 <el-pagination>,连翻页逻辑都省了大半!


二、正确姿势:3步实现专业分页

第1步:注册分页插件(最容易漏的一步!)

新建配置类 MybatisPlusConfig.java

@Configuration
@MapperScan("com.yourpackage.mapper") // 替换为你的 mapper 包路径
public class MybatisPlusConfig {

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        // 添加分页插件,指定数据库类型
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}

✅ 重点

  • 必须使用 MybatisPlusInterceptor(新版 MP 推荐)
  • 旧版 PaginationInterceptor 已废弃!
  • 这一步只需做一次,全局生效!

第2步:Controller 调用分页方法

@GetMapping("/list")
public IPage<Student> list(
    @RequestParam(defaultValue = "1") int current,
    @RequestParam(defaultValue = "10") int size,
    String name) {

    // 1. 构建分页对象
    Page<Student> page = new Page<>(current, size);

    // 2. 构建查询条件
    QueryWrapper<Student> qw = new QueryWrapper<>();
    qw.like(StringUtils.isNotBlank(name), "name", name);

    // 3. 执行分页查询(核心!)
    return studentMapper.selectPage(page, qw);
}

返回的 IPage<Student> 结构如下:

{
  "records": [ {...}, {...} ],  // 当前页数据
  "total": 150,                // 总记录数
  "size": 10,                  // 每页大小
  "current": 1,                // 当前页
  "pages": 15                  // 总页数
}

第3步:前端对接(以 Vue3 + Element Plus 为例)

<el-table :data="tableData" />

<el-pagination
  v-model:current-page="currentPage"
  v-model:page-size="pageSize"
  :total="total"
  @current-change="fetchData"
/>
const fetchData = () => {
  axios.get('/student/list', {
    params: { current: currentPage.value, size: pageSize.value }
  }).then(res => {
    tableData.value = res.data.records;
    total.value = res.data.total; // ⚠️ 必须取 total,不是 records.length!
  });
}

三、避坑指南:分页不生效的3大原因

❌ 坑1:没注册分页插件(最常见!)

如果你跳过第1步,selectPage 会退化成普通查询,只返回第一页数据,total=0

✅ 解决:检查是否有 MybatisPlusConfig,且 @Bean 方法返回的是 MybatisPlusInterceptor

❌ 坑2:用了多数据源但没配置插件

如果你用 dynamic-datasource,需要在每个数据源中单独注册插件,或使用全局拦截器。

❌ 坑3:SQL 被手动改写(如 XML 自定义 SQL)

如果在 Mapper XML 中写了 <select id="selectPage">,MP 无法自动注入 COUNT 查询。

✅ 解决:要么用 MP 的 QueryWrapper,要么在 XML 中自己写两条 SQL(不推荐)。


四、性能优化建议

  • 给查询字段加索引:分页底层会先执行 COUNT(*),大表无索引会很慢。
  • 避免深度分页LIMIT 100000, 10 性能极差,可改用“游标分页”(基于 ID > lastId)。
  • 前端限制最大页码:防止用户恶意请求第 999999 页。

五、结语

MyBatis Plus 的分页功能,是它最实用的特性之一。
90% 的新手都因为漏掉配置类而放弃使用,转而手写低效代码。

现在你知道了:一行配置,终身受益

把这篇文章收藏起来,下次分页出问题,直接对照检查!

Logo

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

更多推荐