MyBatis-Plus 3.4.0分页配置全指南从失效排查到最佳实践最近在技术社区看到不少开发者反馈升级MyBatis-Plus到3.4.0及以上版本后原本运行良好的分页功能突然失效。这其实是一个典型的版本兼容性问题——3.4.0版本对分页机制进行了重大重构。本文将带你深入剖析新旧版本差异提供可落地的配置方案并分享几个实际项目中容易忽略的细节。1. 版本变迁为什么你的分页突然失效了MyBatis-Plus在3.4.0版本对插件体系进行了整体重构最显著的变化就是用MybatisPlusInterceptor替代了旧的PaginationInterceptor。这个改动并非简单的类名变更而是整个拦截器机制的升级。核心变化对比特性3.4.0之前版本3.4.0之后版本核心类PaginationInterceptorMybatisPlusInterceptor架构设计独立拦截器拦截器链InnerInterceptor设计配置方式直接实例化通过addInnerInterceptor添加多插件支持需要多个拦截器实例单一拦截器管理多个内部拦截器在实际项目中我们遇到过这样一个案例一个电商平台的订单查询接口在升级后返回了全部订单数据。检查发现配置类中仍然保留着这样的代码Bean public PaginationInterceptor paginationInterceptor() { return new PaginationInterceptor(); }这种配置在3.4.0版本中已经完全失效但编译器不会报任何警告——这是最危险的情况。新版应该改为Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor()); return interceptor; }2. 正确配置新版分页的完整实现方案2.1 基础配置模板对于Spring Boot项目推荐在配置类中声明拦截器Bean。以下是经过生产验证的配置模板Configuration public class MybatisPlusConfig { Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor new MybatisPlusInterceptor(); // 分页插件 PaginationInnerInterceptor paginationInterceptor new PaginationInnerInterceptor(); paginationInterceptor.setMaxLimit(1000L); // 单页最大记录数 paginationInterceptor.setOverflow(true); // 超出页码返回第一页 // 乐观锁插件 OptimisticLockerInnerInterceptor optimisticLocker new OptimisticLockerInnerInterceptor(); interceptor.addInnerInterceptor(paginationInterceptor); interceptor.addInnerInterceptor(optimisticLocker); return interceptor; } }提示新版架构允许在一个拦截器中组合多个功能比如同时启用分页和乐观锁这在旧版本中需要分别配置多个拦截器。2.2 YAML配置参考如果需要通过配置文件调整参数可以在application.yml中添加mybatis-plus: configuration: default-scripting-language: freemarker global-config: db-config: logic-delete-field: deleted # 逻辑删除字段但要注意分页拦截器必须通过Java代码配置这是MyBatis-Plus的设计约束。3. 实战中的六个关键细节Page对象构造陷阱// 错误示例 - 缺少必要参数 PageUser page new Page(); // 正确做法 PageUser page new Page(current, size);COUNT查询优化新版默认会执行COUNT查询可以通过page.setSearchCount(false)禁用PageUser page new Page(1, 10); page.setSearchCount(false); // 不执行SELECT COUNT(1)多数据源场景如果使用动态数据源需要确保拦截器被所有SqlSessionFactory共享Bean ConditionalOnMissingBean public MybatisPlusInterceptor globalInterceptor() { // 配置同上 }自定义分页SQL对于复杂查询可能需要手动编写分页SQLselect idselectComplexPage resultType... WITH temp AS (...) SELECT * FROM temp LIMIT #{offset}, #{pageSize} /select性能监控建议添加以下配置可输出分页SQL日志logging.level.com.baomidou.mybatisplusDEBUG与其它插件冲突如果同时使用TenantLineInnerInterceptor多租户需要注意添加顺序// 正确的顺序多租户 - 分页 interceptor.addInnerInterceptor(new TenantLineInnerInterceptor()); interceptor.addInnerInterceptor(new PaginationInnerInterceptor());4. 进阶自定义分页行为新版架构提供了更灵活的扩展点。例如实现方言适配public class CustomPaginationInterceptor extends PaginationInnerInterceptor { Override protected String getDialectClause(long offset, long limit) { // 适配Oracle分页语法 return FROM (SELECT ROW_.*, ROWNUM ROWNUM_ FROM ( %s ) ROW_ WHERE ROWNUM ?) WHERE ROWNUM_ ?; } }再比如修改默认的页码处理逻辑paginationInterceptor.setOverflowHandler(page - { // 超出最大页时返回空集合而非第一页 page.setRecords(Collections.emptyList()); return page; });这些扩展能力在旧版本中都需要通过复杂的AOP实现现在通过标准接口就能完成。5. 版本兼容方案对于需要同时支持新旧版本的项目比如组件库可以通过条件判断实现兼容Bean ConditionalOnClass(name com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor) public MybatisPlusInterceptor newInterceptor() { // 3.4.0配置 } Bean ConditionalOnMissingClass(com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor) public PaginationInterceptor oldInterceptor() { // 旧版配置 }6. 测试验证方案配置完成后建议通过以下用例验证Test void testPagination() { PageUser page userService.page(new Page(1, 2)); assertThat(page.getRecords()).hasSize(2); assertThat(page.getTotal()).isGreaterThan(0); // 验证SQL日志是否包含LIMIT ListString sqlStatements sqlLogCollector.getLoggedQueries(); assertThat(sqlStatements.get(0)).contains(LIMIT); }遇到问题时可以按这个检查清单排查确认依赖版本 ≥ 3.4.0检查拦截器是否被正确注册验证Page对象构造参数检查是否有其他拦截器干扰查看执行的SQL日志在最近的一个金融项目中我们遇到分页失效的根本原因是团队自行开发的审计拦截器修改了SQL执行流程。通过拦截器优先级调整解决了这个问题interceptor.addInnerInterceptor(new AuditInterceptor(), 0); // 最高优先级 interceptor.addInnerInterceptor(new PaginationInnerInterceptor(), 1);