1. 分页查询的核心价值与实现思路在业务系统开发中数据分页是最基础却至关重要的功能。我经历过多个从零搭建的企业级项目发现无论管理系统还是移动应用只要涉及数据展示就必然需要分页支持。传统JDBC实现分页需要手动计算limit/offset而MyBatis-Plus的IPage接口让这一切变得异常简单。SpringBoot与MyBatis-Plus的组合堪称Java后端开发的黄金搭档。前者提供自动化配置和依赖管理后者在MyBatis基础上扩展出强大的单表操作能力。当它们相遇时原本需要数十行代码的分页查询现在只需3个关键步骤定义分页参数对象调用Service层分页方法返回统一分页结果格式这种简洁性背后是MyBatis-Plus对物理分页的智能处理。它会根据当前数据库类型MySQL/Oracle等自动生成适配的分页SQL开发者无需再关心不同数据库的语法差异。下面通过完整示例演示如何实现企业级分页方案。2. 环境准备与基础配置2.1 依赖引入关键点在pom.xml中需要明确引入以下核心依赖建议锁定版本号dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.3/version /dependency dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-extension/artifactId version3.5.3/version /dependency注意mybatis-plus-extension包含分页插件等增强功能必须显式引入。常见错误是只引入starter导致分页失效。2.2 分页插件配置在SpringBoot启动类或配置类中添加分页拦截器Configuration public class MybatisPlusConfig { Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor new MybatisPlusInterceptor(); // 分页插件 interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } }这里指定DbType.MYSQL会根据MySQL语法生成分页SQL。实际项目中应根据真实数据库类型配置支持Oracle、PostgreSQL等主流数据库。3. 核心实现步骤详解3.1 定义分页参数接收对象创建统一的分页查询参数DTOData public class PageQuery { ApiModelProperty(当前页码) private Integer pageNum 1; ApiModelProperty(每页数量) private Integer pageSize 10; public T IPageT buildPage() { return new Page(pageNum, pageSize); } }这个对象有两个关键设计点设置默认值避免空指针提供buildPage方法快速创建IPage实例3.2 Service层分页查询实现以用户查询为例展示Service实现Service public class UserServiceImpl extends ServiceImplUserMapper, User implements UserService { Override public PageResultUser queryByPage(PageQuery query) { // 构建分页条件 IPageUser page query.buildPage(); // 执行分页查询 IPageUser result baseMapper.selectPage(page, null); // 转换为统一返回格式 return new PageResult( result.getRecords(), result.getTotal(), result.getCurrent(), result.getSize() ); } }这里使用MyBatis-Plus提供的selectPage方法其第二个参数是Wrapper条件构造器传null表示无附加条件。3.3 统一分页响应结构定义标准化分页返回体Data AllArgsConstructor public class PageResultT { private ListT records; private long total; private long current; private long size; }这种结构适配前端主流分页组件如ElementUI的Pagination包含四个必要字段records当前页数据列表total总记录数current当前页码size每页条数4. 高级功能与实战技巧4.1 带条件的分页查询实际项目中最常见的是带过滤条件的分页通过QueryWrapper构建条件Override public PageResultUser queryByCondition(UserQuery query) { // 构建分页条件 IPageUser page query.buildPage(); // 构建查询条件 QueryWrapperUser wrapper new QueryWrapper(); wrapper.like(StringUtils.isNotBlank(query.getName()), name, query.getName()) .eq(query.getStatus() ! null, status, query.getStatus()); // 执行查询 IPageUser result baseMapper.selectPage(page, wrapper); return new PageResult(result.getRecords(), result.getTotal(), result.getCurrent(), result.getSize()); }条件构造器的like、eq等方法第一个参数是boolean类型实现动态SQL效果。这是MyBatis-Plus最强大的特性之一。4.2 自定义分页SQL处理对于复杂多表查询可能需要自定义SQL!-- UserMapper.xml -- select idselectUserPage resultTypeUser SELECT u.*, d.dept_name FROM user u LEFT JOIN department d ON u.dept_id d.id ${ew.customSqlSegment} /selectMapper接口定义IPageUser selectUserPage(IPageUser page, Param(Constants.WRAPPER) WrapperUser wrapper);关键点必须使用${ew.customSqlSegment}接收Wrapper生成的WHERE条件否则分页条件不会生效4.3 性能优化实践COUNT优化对于大表分页默认COUNT(*)可能很慢。可以通过重写selectPage方法使用缓存计数Override public PageResultUser fastPageQuery(PageQuery query) { IPageUser page query.buildPage(); // 使用缓存的总数 Long total getCachedTotalCount(); page.setTotal(total); // 只查询当前页数据 ListUser records baseMapper.selectPageRecords( page.getCurrent(), page.getSize() ); return new PageResult(records, total, page.getCurrent(), page.getSize()); }深度分页处理当pageNum较大时如100页MySQL的LIMIT offset,size效率急剧下降。解决方案使用基于游标的分页记录上一页最后一条记录的ID禁止跳转到过大的页码5. 常见问题排查指南5.1 分页失效问题现象返回所有记录而非分页数据排查步骤检查是否配置了PaginationInnerInterceptor确认Mapper方法第一个参数是IPage类型查看执行的SQL日志是否包含LIMIT子句5.2 总数统计异常现象total数量与实际不符解决方案检查Wrapper条件是否正确复杂SQL可能需要自定义COUNT语句select idselectUserPage resultTypeUser SELECT u.* FROM user u ${ew.customSqlSegment} /select select idselectUserPageCount resultTypelong SELECT COUNT(1) FROM user u ${ew.customSqlSegment} /select5.3 排序失效问题现象ORDER BY不生效正确用法wrapper.orderBy(true, true, create_time) .orderBy(true, false, name);第一个参数表示是否执行排序第二个参数表示是否ASC第三个是字段名6. 最佳实践总结经过多个项目验证推荐以下分页实现规范统一参数与响应所有分页接口使用相同的PageQuery和PageResult结构防御性编程对pageSize设置最大值限制如不超过100前端协作返回的current应该是当前实际页码有些前端组件从0开始计数需要转换日志完善在Service层记录关键分页参数便于问题排查性能监控对慢分页查询建立告警机制在最近开发的电商后台系统中这套分页方案支撑了日均百万级的查询请求。特别是在商品管理模块面对千万级商品表通过缓存总数游标分页的组合方案使分页响应时间始终保持在200ms以内。