
在 Spring Boot 项目中使用 MyBatis-Plus (MP) 进行分页查询核心在于分页插件的配置和**IPage**** /**Page**对象的使用**。以下是一份完整的、涵盖各种常见用法的实战示例演示。1. 基础配置必须MyBatis-Plus 的分页功能不是默认开启的必须手动注册拦截器。1.1 添加依赖dependencygroupIdcom.baomidou/groupIdartifactIdmybatis-plus-spring-boot3-starter/artifactId!-- Boot2用 mybatis-plus-boot-starter --version3.5.7/version/dependency1.2 注册分页插件ConfigurationMapperScan(com.example.mapper)publicclassMybatisPlusConfig{/** * 新版 3.4.0 推荐写法 */BeanpublicMybatisPlusInterceptormybatisPlusInterceptor(){MybatisPlusInterceptorinterceptornewMybatisPlusInterceptor();// ⚠️ 注意如果同时使用了多租户或乐观锁插件分页插件建议放在最后PaginationInnerInterceptorpaginationInterceptornewPaginationInnerInterceptor(DbType.MYSQL);// 设置最大单页限制数量默认 500 条-1 不受限制paginationInterceptor.setMaxLimit(500L);// 溢出处理页码超出总页数时是否跳到第一页paginationInterceptor.setOverflow(false);interceptor.addInnerInterceptor(paginationInterceptor);returninterceptor;}}2. 实体与 Mapper 准备DataTableName(sys_user)publicclassUser{TableId(typeIdType.AUTO)privateLongid;privateStringusername;privateIntegerage;privateStringemail;privateLocalDateTimecreateTime;}MapperpublicinterfaceUserMapperextendsBaseMapperUser{// 自定义分页方法XML或注解SQLIPageUserselectUserPage(IPageUserpage,Param(age)Integerage);}3. 各种分页用法示例✅ 用法一基础条件分页最常用使用 MP 内置的selectPageLambdaQueryWrapper。ServicepublicclassUserService{AutowiredprivateUserMapperuserMapper;publicIPageUsergetUserList(intpageNum,intpageSize,Stringusername){// 1. 构建分页参数PageUserpagenewPage(pageNum,pageSize);// 2. 构建查询条件LambdaQueryWrapperUserwrappernewLambdaQueryWrapper();wrapper.like(StrUtil.isNotBlank(username),User::getUsername,username).orderByDesc(User::getCreateTime);// 3. 执行分页查询returnuserMapper.selectPage(page,wrapper);}}✅ 用法二自定义 SQL 分页当复杂查询多表关联、统计字段无法用 Wrapper 表达时在 XML 中写 SQLMP 会自动拦截并追加LIMIT和COUNT。Mapper:IPageUserVoselectCustomPage(IPageUserVopage,Param(deptId)LongdeptId);XML:!-- ⚠️ 注意只需要写业务SQL不要手写 LIMITMP会自动处理 --selectidselectCustomPageresultTypecom.example.vo.UserVoSELECT u.id, u.username, d.dept_name, COUNT(o.id) as order_count FROM sys_user u LEFT JOIN sys_dept d ON u.dept_id d.id LEFT JOIN sys_order o ON u.id o.user_id WHERE u.dept_id #{deptId} GROUP BY u.id, u.username, d.dept_name ORDER BY order_count DESC/select✅ 用法三返回 VO/DTO 的分页转换数据库查出来是 Entity但前端需要 VO。不要先查全部再手动转利用 MP 的convert方法。publicIPageUserVOgetUserVoPage(intpageNum,intpageSize){PageUserentityPagenewPage(pageNum,pageSize);LambdaQueryWrapperUserwrappernewLambdaQueryWrapperUser().eq(User::getAge,25);// 查出 Entity 分页结果IPageUseruserPageuserMapper.selectPage(entityPage,wrapper);// 一行代码转换为 VO 分页对象保留分页元数据IPageUserVOvoPageuserPage.convert(user-{UserVOvonewUserVO();BeanUtils.copyProperties(user,vo);vo.setDisplayName(user.getUsername()(user.getAge()));returnvo;});returnvoPage;}✅ 用法四Map 方式接收分页结果适用于动态列、报表等场景。publicIPageMapString,ObjectgetMapPage(intpageNum,intpageSize){PageMapString,ObjectpagenewPage(pageNum,pageSize);QueryWrapperUserwrappernewQueryWrapper();wrapper.select(id,username,age).gt(age,20);returnuserMapper.selectMapsPage(page,wrapper);}✅ 用法五仅查询列表 / 仅查询总数避免不必要的 COUNT 查询提升性能。// 只查列表不执行 SELECT COUNT(*)publicListUsergetListWithoutCount(intpageNum,intpageSize){PageUserpagenewPage(pageNum,pageSize,false);// 第三个参数设为 falsereturnuserMapper.selectPage(page,null).getRecords();}// 只查总数publiclonggetCount(){LambdaQueryWrapperUserwrappernewLambdaQueryWrapperUser().eq(User::getAge,25);returnuserMapper.selectCount(wrapper);}4. Controller 层统一响应封装RestControllerRequestMapping(/api/users)publicclassUserController{AutowiredprivateUserServiceuserService;GetMapping(/page)publicRIPageUserVOpage(RequestParam(defaultValue1)intpageNum,RequestParam(defaultValue10)intpageSize,RequestParam(requiredfalse)Stringusername){IPageUserVOresultuserService.getUserVoPage(pageNum,pageSize);returnR.ok(result);}}返回 JSON 结构示例{code:200,data:{records:[...],// 当前页数据列表total:156,// 总记录数size:10,// 每页大小current:1,// 当前页码pages:16,// 总页数searchCount:true// 是否进行了count查询}}5. ⚠️ 常见坑点与注意事项问题原因 解决方案分页不生效返回全部数据未注册MybatisPlusInterceptor或 DbType 设置错误自定义SQL分页报错XML 中手写了LIMIT导致 MP 拼接出双重 LIMIT去掉手写 LIMITCOUNT 查询很慢复杂关联查询的 COUNT 优化可在 XML 中单独写select idxxx_mpCount覆盖默认 COUNT 逻辑深分页性能差LIMIT 1000000, 10依然慢改用游标分页WHERE id lastId LIMIT 10或 ES多线程下 Page 对象复用Page对象非线程安全每次请求必须new Page()不要定义为成员变量overflow 配置当请求页码 总页数时overflowtrue回到首页false返回空列表6. IPage 核心属性速查IPageUserpageuserMapper.selectPage(newPage(1,10),null);page.getRecords();// 数据列表page.getTotal();// 总记录数page.getSize();// 每页条数page.getCurrent();// 当前页码page.getPages();// 总页数page.hasNext();// 是否有下一页page.hasPrevious();// 是否有上一页以上示例覆盖了 MyBatis-Plus 分页在日常开发中的绝大多数场景可直接复制到项目中使用。