必须用PaginationInnerInterceptor配Oracle方言,因MyBatis-Plus 3.4.0+已废弃PaginationInterceptor;拦截器必须指定DbType.ORACLE,否则生成LIMIT而非ROWNUM导致报错或空结果;多数据源需隔离配置,禁用PageHelper,Page构造须显式传pageNum和pageSize,并开启DEBUG日志验证SQL含ROWNUM。

必须用 PaginationInnerInterceptor 配 Oracle 方言
MyBatis-Plus 3.4.0+ 已废弃 PaginationInterceptor,硬写它会直接失效。Oracle 分页依赖 ROWNUM 嵌套结构,拦截器必须明确告知数据库类型,否则生成的 SQL 还是 LIMIT,一执行就报错或返回空。
正确配置只有一行关键:interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.ORACLE))。写成 DbType.MYSQL 或漏掉 DbType 参数,SQL 就不会重写为 WHERE ROWNUM ? 形式。
- 多数据源项目不能共用一个
MybatisPlusInterceptor实例,需按数据源隔离配置 - 如果同时引入了
pagehelper-spring-boot-starter,必须彻底排除——两个分页插件叠加会导致 SQL 被重写两次,结果不可预测 - 验证是否生效:开启
logging.level.com.yourpackage.mapper=DEBUG,看日志里实际执行的 SQL 是否含ROWNUM关键字
Page 构造必须显式传 pageNum 和 pageSize
Page<user> page = new Page()</user> 看似简洁,但它是隐患源头。虽然默认 current=1、size=10,但在 Oracle 场景下(尤其搭配 YashanDB 或某些驱动版本),这些默认值可能被忽略,导致查出全表数据或空集合。
安全写法永远是:Page<user> page = new Page(pageNum, pageSize)</user>。注意:pageNum 从 1 开始,传 0 或负数会触发异常或静默归零。
- 前端传参不可信,
pageNum和pageSize应在 Controller 层做校验,比如pageNum 则抛 <code>IllegalArgumentException - 若业务允许“无总页数”模式(如仅提供“下一页”按钮),调用前加
page.setSearchCount(false),避免额外COUNT(*)扫描大表 - 确保
COUNT查询能命中索引,否则 Oracle 全表扫描开销极高;必要时建覆盖索引
分页 SQL 实际长什么样?别只信文档
MyBatis-Plus 对 Oracle 的物理分页不是黑盒。它最终生成的 SQL 是三层嵌套结构,类似:
SELECT * FROM (
SELECT TMP_PAGE.*, ROWNUM RN FROM (
SELECT * FROM USER WHERE STATUS = 'ACTIVE'
) TMP_PAGE WHERE ROWNUM <= ?
) WHERE RN > ?外层 RN > ? 保证跳过前 N 条,内层 ROWNUM 控制上限。这个结构对 Oracle 是必须的,因为 <code>ROWNUM 是在结果集生成时动态分配的,不能直接写 WHERE ROWNUM BETWEEN ? AND ?。
- 如果你在日志里看到的是单层
SELECT ... WHERE ROWNUM ,说明分页没生效,大概率是拦截器没配对或 <code>Page没传参 - 自定义 SQL(如
@Select)不会被自动分页,除非手动在 XML 中写<bind></bind>或用RowBounds,但那已脱离 MyBatis-Plus 分页体系 - 关联查询分页要格外小心:
COUNT子句和主查询的JOIN逻辑必须一致,否则总数和列表数据不匹配
为什么 total 查询慢?Oracle 的 COUNT 不是免费的
MyBatis-Plus 默认每次分页都执行一条 SELECT COUNT(*),Oracle 上这句可能比主查询还慢,尤其当表大、没走索引、或 WHERE 条件涉及函数/表达式时。
这不是框架 bug,而是 Oracle 优化器对 COUNT(*) 的处理机制决定的——它往往需要访问所有符合条件的数据块,无法单纯靠索引快速估算。
- 确认
COUNT是否走索引:用EXPLAIN PLAN查看执行计划,重点看access和filter是否落在索引列上 - 如果业务场景允许模糊总数(如“约 5000+ 条”),可关掉计数:
page.setSearchCount(false) - 对高频分页的大表,考虑用异步任务定期缓存总数,或改用游标分页(
WHERE id > ? ORDER BY id LIMIT ?)绕过COUNT
Oracle 分页真正卡点不在语法,而在 COUNT 性能和多数据源隔离。很多人配对了 DbType.ORACLE 就以为万事大吉,结果上线后发现首页快、翻到第 50 页就超时——那八成是 COUNT 没压住。


















