新闻详情

新闻详情

首页 / 资讯中心 / 详情

MybatisPlus分页失效与500条限制:原理、排查与避坑指南

发布时间:2026/9/26 22:50:56来源:尧图网络
MybatisPlus分页失效与500条限制:原理、排查与避坑指南
先说个真实场景上周同事小周丢过来一段代码说MybatisPlus分页出邪门问题了——pageSize传1000查出来只有500条传5000结果还是500条。更气人的是换了个查询方法又变成全表数据一起返回分页完全没生效。一说这个群里好几个老哥都冒出来有人说遇到过单页500条限制有人说分页莫名失效最后发现是拦截器没配。这两个问题大概是MybatisPlus实战里出现频率最高的两个翻车点而且表现形式特别容易混淆一个是不分页一个是单页被钳制。这篇就专门聊聊这两个问题背后的原理、完整的排查思路以及怎么一次性避开。适合刚接入MybatisPlus的新手看也适合那种项目分页突然坏了不知道从哪查起的老手直接对照排查。1. 分页不是引入依赖就能用先搞清楚PaginationInnerInterceptor在干嘛MybatisPlus的分页插件核心是一个叫PaginationInnerInterceptor的拦截器组件。它的作用本质上就是拦截Mybatis框架已经解析好的SQL把它改写成两条SQL一条是SELECT COUNT(*)的count查询用来算总条数另一条是拼接了LIMIT、OFFSET的分页查询用来查当前页数据。改写完之后再把结果塞回Page对象里你就能拿到records、total、current这些字段了。你可以把MybatisPlusInterceptor想象成一个小区门口的保安亭PaginationInnerInterceptor是其中一个值班保安。保安亭里可以同时站好几个保安比如租户拦截器、乐观锁拦截器、分页拦截器。但前提是保安得真的站在亭子里他才会上前拦车检查。很多人的项目分页失效问题就出在这——代码里只引了mybatis-plus-boot-starter依赖但从来没往保安亭里安排分页保安。MybatisPlus在3.4.0版本之后官方重构了插件机制分页能力必须通过MybatisPlusInterceptor显式注册才能生效。这也是很多老项目在升级依赖之后突然分页失效的根本原因——以前老版本里可能某个自动配置帮你把分页弄好了升级之后机制变了你没跟上分页就罢工了。标准的注册方式是这样Configuration public class MybatisPlusConfig { Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor new MybatisPlusInterceptor(); PaginationInnerInterceptor paginationInnerInterceptor new PaginationInnerInterceptor(DbType.MYSQL); interceptor.addInnerInterceptor(paginationInnerInterceptor); return interceptor; } }这里有个细节非常值得注意DbType一定要和你实际用的数据库对上。你连的是MySQL就写DbType.MYSQL连的是PostgreSQL就写DbType.POSTGRE_SQL。如果写错了分页插件生成的方言SQL就是错的轻则分页数据不对重则直接SQL报错。我见过有同事在Oracle项目里配了MySQL方言跑出来的分页SQL带着LIMIT关键字Oracle根本不认直接报错排查了半天。还有一种配置了但等于没配的情况Configuration类放在了Spring Boot启动类所在包的扫描范围之外。Spring Boot默认只扫描启动类所在包以及它的子包如果你的MybatisPlusConfig放到了别的目录层级里容器压根不会加载它Bean自然也就不会执行。这种问题从代码上看非常无辜——注解、配置类、Bean方法全都写对了但就是没生效。排查方式很简单在配置类的构造函数里打一行日志启动项目的时候看这行日志打没打出来。没打出来那肯定是类没被扫描进容器直接挪位置或者加ComponentScan指定扫描路径就行。2. 分页失效的完整排查链按顺序来十分钟定位问题分页失效是个特别笼统的现象病根可能完全不同。如果你一上来就瞎改配置大概率会越改越乱。我的建议是按照下面这个顺序一步步验证每验证一步就能排除一类原因。第一步把SQL日志打开先看执行的SQL到底长什么样。在application.yml里加上这段配置mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl然后重启项目翻一下控制台日志。重点是看查询语句后面有没有LIMIT。如果SQL是完整地、不带任何前缀地把全表查询打出来了说明拦截器压根没介入——问题出在拦截器注册或配置上。如果SQL日志里能看到类似LIMIT 1,10这样的片段说明分页插件已经工作了那问题就不在于没分页而在于其他环节比如total不对、page参数没对上等等。这一步能快速把问题劈成两半非常关键。第二步检查拦截器是否真的注册了。确认配置类存在确认PaginationInnerInterceptor确实通过addInnerInterceptor加到了MybatisPlusInterceptor里再确认配置类在Spring的扫描范围内。一个很实用的土办法在MybatisPlusInterceptor的Bean方法里加一行System.out.println(分页拦截器已注册)启动时看有没有输出。没输出就往扫描路径上找原因。第三步检查Page对象到底有没有传进Mapper方法。这是新手最容易踩的坑而且代码看起来极其无辜// 错误写法Page对象创建了但压根没传给Mapper PageUser page new Page(1, 10); ListUser userList userMapper.selectList(null);你以为new了一个Page分页就自动生效了不是的。MybatisPlus的分页机制有一个隐含约定Mapper的方法参数里必须包含Page或IPage类型的对象拦截器才能从参数里拿到current和sizeSQL改写的时候才有数据可拼。正确写法是// 正确写法把Page作为Mapper方法的参数传进去 PageUser page userMapper.selectPage(new Page(1, 10), null);如果你用自定义SQLMapper接口的方法签名要这样写IPageUserVO selectUserPage(PageUserVO page, Param(status) Integer status);对应的Mapper.xml里不需要任何LIMIT插件会自动拼select idselectUserPage resultTypecom.example.UserVO SELECT * FROM user WHERE status #{status} /select注意SQL里千万别自己写死LIMIT。插件会在你写的SQL后面再拼一个LIMIT如果XML里已经有一个就会变成LIMIT 10 LIMIT 0,10这种语法错误或者顺序错乱导致数据完全不对。这是自定义SQL分页里非常容易翻车的点。第四步检查count查询有没有出错。如果日志显示分页查询本身带了LIMIT但返回的total数字不对那问题多半出在MybatisPlus自动生成的count SQL上。插件生成count语句时一般做法是把原SQL中的SELECT 字段列表替换成SELECT COUNT(*)但对于一些复杂SQL比如含DISTINCT、GROUP BY、UNION、多表LEFT JOIN的情况自动生成的结果往往是不对的。这里补一张排查对照表方便你对症下手现象可能原因验证方式SQL不带LIMIT全表返回拦截器没注册或Page没传进Mapper看SQL日志、检查配置类、检查方法参数SQL带LIMIT但total不对自动count SQL写错或JOIN导致计数重复打印日志看COUNT语句单页数量被封顶比如永远只有500条PaginationInnerInterceptor设置了maxLimit检查配置调用getMaxLimit()查看分页查询很慢越往后翻越慢深分页OFFSET太大改用游标分页或延迟关联排查的时候对着这张表走一遍基本能覆盖90%的分页异常场景。剩下的10%就是下面要单独拿出来说的500条限制这种隐蔽配置。3. 单页500条限制到底是谁干的一个被名字耽误的安全机制网上搜mybatisplus单页500条限制能搜出一堆求助帖。有意思的是MybatisPlus本身并没有写死只能查500条这个限制通常只可能是被人为配置出来的。罪魁祸首就是PaginationInnerInterceptor里的maxLimit属性PaginationInnerInterceptor paginationInnerInterceptor new PaginationInnerInterceptor(DbType.MYSQL); paginationInnerInterceptor.setMaxLimit(500L); interceptor.addInnerInterceptor(paginationInnerInterceptor);一旦配置了setMaxLimit(500L)不管前端传的pageSize是1000、5000还是10000分页插件内部会把单页条数强制钳制到500。原理很简单插件在处理分页参数时会做一次size Math.min(pageSize, maxLimit)的运算把过大的每页条数压到上限值。所以你的接口永远只能返回500条不是数据库的问题也不是网络问题是配置层的限制。那官方为什么要设计这个参数说白了这是一个非常实用的自我保护机制。你想如果接口不小心暴露到了公网有人写个脚本疯狂请求你的分页接口每次把pageSize传成9999999数据库为了执行LIMIT 9999990, 10这种语句需要先扫描近一千万行数据再丢掉CPU和IO直接被打满。maxLimit的作用就是在业务层面给单页数据量做一个保险丝防止这类请求拖垮数据库。所以很多团队的安全规范里会明确要求给分页插件设置一个单页上限500是最常见的约定值之一。如果你确认业务确实需要单页返回超过500条那就得解除这个限制。解除方式也简单把maxLimit设置成-1就行paginationInnerInterceptor.setMaxLimit(-1L);设置成-1之后插件就不再对单页条数做钳制了。不过我建议除非你确定自己的接口不会被打否则别完全放开宁可设置成一个更合理的业务上限比如10000也不要设置成无限制。我看到过太多解除限制之后接口被人刷爆的案例。再补一个容易混淆的知识点如果你发现前端表格只显示了500行但接口返回的数据条数其实是对的那问题可能不在MybatisPlus而在前端组件。很多前端表格框架默认渲染500行或者你使用的数据库管理工具比如Navicat、DataGrip默认查询结果也只显示500行。这三种500条是完全不同层面的问题场景数据是否已查出解决方向MybatisPlusmaxLimit限制未查出SQL里的LIMIT就是500修改setMaxLimit(-1L)或调大上限前端表格默认渲染限制已查出接口返回完整调整前端表格组件分页配置数据库管理工具显示限制已查出工具只显示前500行修改工具偏好设置排查的时候先用接口测试工具比如Postman直接调一下接口看返回的JSON里records数组的长度。如果接口返回的条数本来就超过500条那和MybatisPlus一点关系都没有问题在前端。另外即使你解除了500条限制我也得提醒一句深分页真正的敌人不是单页条数上限而是翻页深度。LIMIT 490000, 10这条SQLMySQL照样得先读前490010行再把前490000行丢掉最后给你返回10行。翻得越深性能越差。如果业务确实需要翻很多页更合理的方案是改用游标分页也就是记住上一页最后一条记录的ID下一页只查ID大于它的记录。或者用延迟关联先通过覆盖索引查出当前页的主键再回表拿完整数据SELECT t.* FROM user t INNER JOIN ( SELECT id FROM user WHERE status 1 ORDER BY id LIMIT 490000, 10 ) tmp ON t.id tmp.id ORDER BY t.id;这样MySQL在子查询里只用索引扫描不需要回表性能会好非常多。这个话题以后再展开这里先记住结论500条上限可以解除但深分页的性能问题靠的是方案设计不是无脑调大pageSize。4. 还有几个分页翻车场景一个个排雷排查过上面两大类问题之后项目里的分页基本能稳定运行了。但实战里还有几个非常隐蔽的翻车点我平时帮人看代码的时候没少遇到这里一并列出来。场景一多表关联查询分页total统计重复。比如查询用户列表需要LEFT JOIN用户的角色表、部门表。如果用户有多条角色记录JOIN之后会产生多行MybatisPlus自动生成的count统计会把关联出来的重复行也算进去导致total虚高。解决办法有两个方向一是把自动count换成自定义count在Mapper里单独提供一个count方法让分页插件优先用它二是调整SQL写法在子查询里先聚合。我个人更推荐自定义count的方式逻辑清晰也方便维护。select idselectUserRolePage resultTypecom.example.vo.UserVO SELECT u.id, u.name, r.role_name FROM user u LEFT JOIN role r ON u.role_id r.id WHERE u.status #{status} /select select idselectUserRolePageCount resultTypejava.lang.Long SELECT COUNT(*) FROM user u WHERE u.status #{status} /select场景二Page对象被复用导致翻页错乱。有些同学会在循环里反复使用同一个Page对象比如做批量导出的时候想着每循环一次自动翻一页结果翻来翻去查出来的都是第一页。这是因为分页插件每次都从Page对象里读取当前的current和size你复用同一个Page它不会自己帮你累加页码。正确做法是每次查询都新建一个Page或者在循环里显式调用page.setCurrent(current)更新页码。这个问题的根源还是对Page对象的生命周期没理解透它只是一个承载分页参数的普通对象不是分页游标。场景三分页查询没有稳定的ORDER BY出现数据重复或丢失。MySQL在未指定排序字段的情况下返回顺序是不保证的。两次查询同一页数据如果中间恰好有数据写入返回结果可能就不一致。对于分页功能来说这是一个很严重的隐患——用户在第一页看到的数据刷新一下跑到了第二页。解决方式很简单分页查询一定要显式指定ORDER BY而且最好用主键或者有唯一索引的字段不要只按创建时间排因为时间可能重复。选择排序字段时优先考虑唯一性稳定的字段。场景四导出大表数据时反复分页内存溢出。分页查询本身是好的但如果用来导出几十万条数据每次都把List攒到内存里最后一起写文件很容易把JVM堆内存撑爆。这种场景更适合使用MybatisPlus的游标查询能力——它基于JDBC的流式读取每次只从数据库取少量数据处理完一批再取下一批不会一次性把全量数据加载到内存。游标查询的具体用法是返回类型用CursorT注意使用完后要关闭游标释放数据库连接这个细节很多人会漏掉。最后给一张自检清单你可以直接保存下来下次分页出问题的时候逐项核对拦截器里是否注册了PaginationInnerInterceptorDbType是否和数据库匹配Page对象是否作为参数传入了Mapper方法maxLimit是否被设置成了不合理的值复杂SQL的count统计是否准确分页查询是否指定了稳定的排序字段。我在实际项目里看过太多分页相关的bug总结下来大部分不是框架的问题而是某个环节的约定没对齐——要么是配置漏了要么是参数没传对要么是被某个隐蔽的配置限制住了。尤其是maxLimit这个参数它被设置的时候往往是出于好心但时间一长团队里没人记得这一茬排查起来特别费劲。所以建议你接手一个老项目的时候第一件事就是把分页插件的配置打印出来看一眼getMaxLimit()这个方法可比你对着控制台猜半天快多了。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

Pandas set_index 完全指南:从索引机制到实战性能优化 2026/9/26 23:39:28

Pandas set_index 完全指南:从索引机制到实战性能优化

学 pandas 的人,早晚会撞上set_index这个名字。我第一次认真研究它,是在处理一份几十万行的订单表时,业务方要求按订单号秒级查询,一开始我只会df[df[order_id] xxx],每次都慢得受不了。后来把order_id设成索引&#…

阅读更多 →
亲测3款wordpress简单的博客主题避坑指南 2026/9/26 23:39:09

亲测3款wordpress简单的博客主题避坑指南

亲测3款wordpress简单的博客主题避坑指南 找过建站公司的朋友,心里都有一本难念的经。明明是个简单的个人博客或内容站,报价单递过来直接吓一跳,几万块起步还不敢还价。怕代码烂、怕维护难、怕后期被软件商“绑架”交高昂年费。这种对高价的恐惧…

阅读更多 →
开发网站和application新手入门:搞定备案与部署避坑指南 2026/9/26 23:39:02

开发网站和application新手入门:搞定备案与部署避坑指南

开发网站和application新手入门:搞定备案与部署避坑指南 备案流程一头雾水,是不是让你对着“接入商”、“主体信息”这些词就头大?很多新手在开发网站和application的起步阶段,最头疼的不是代码写不出来,而是卡在合规上线这一步。…

阅读更多 →
3步搞定网站建设公司简介模板下载安全从零搭建 2026/9/26 23:38:56

3步搞定网站建设公司简介模板下载安全从零搭建

3步搞定网站建设公司简介模板下载安全从零搭建 改个需求建站公司拖一周,这种憋屈感谁懂?很多老板发现,那些花大价钱买的“高端官网”,连个公司简介的下载按钮都藏着后门。别急着骂人,今天咱们不聊虚的,直接拆解 网站建设公司简介模板下载…

阅读更多 →
从手敲prompt到一条命令:Claude Code模板搭建实战指南 2026/9/26 23:38:56

从手敲prompt到一条命令:Claude Code模板搭建实战指南

从"每次重复敲prompt"到"一条命令搞定":聊聊我整理 claude-code 模板的那些事用了半年多 Claude Code,我最深的感受就一句话:这个工具的下限取决于你会不会写 prompt,上限则取决于你有没有积累模板。一开始我…

阅读更多 →
AI写代码前先闭环需求:Grill-Me、BFS、AFK实操指南 2026/9/26 23:38:56

AI写代码前先闭环需求:Grill-Me、BFS、AFK实操指南

我见过太多人把“让 AI 写代码”当成“把一句话丢给 AI 工具就撒手不管”的活儿。刚开始确实爽,屏幕上几十行代码瞬间蹦出来,比自己敲快多了。等到联调的时候才发现,AI 替你做了无数个你没说出口的假设——表结构这么建、字段那么叫、异常随便…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉