必须配置PaginationInnerInterceptor并指定DbType.ORACLE,否则分页SQL误用LIMIT导致报错或空结果;多数据源需隔离配置;Page构造须显式传参且current≥1;Oracle分页必须添加唯一字段排序以保证ROWNUM稳定性。

必须配置 PaginationInnerInterceptor 并指定 DbType.ORACLE
MyBatis-Plus 不会自动识别数据库类型,Oracle 分页依赖 PaginationInnerInterceptor 的方言支持。如果漏配或错配为 DbType.MYSQL,分页 SQL 会被重写成 LIMIT 形式,直接报错或返回空结果。
正确配置方式如下:
@Configuration
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.ORACLE));
return interceptor;
}
}
- 必须使用
PaginationInnerInterceptor(不是已废弃的PaginationInterceptor) -
DbType.ORACLE是硬编码常量,不能传字符串"oracle" - 若项目含多数据源,需确保该拦截器作用于 Oracle 数据源对应的
SqlSessionFactory
Oracle 分页必须带确定性排序,否则翻页重复
Oracle 分页底层用的是 ROWNUM 嵌套子查询,当 ORDER BY 字段存在重复值(如多个记录 create_time 相同),两次查询的物理排序顺序可能不一致,导致第 2 页开头几条和第 1 页末尾几条重复。
解决方法是强制在排序条件末尾追加主键或唯一字段:
LambdaQueryWrapper<user> wrapper = new LambdaQueryWrapper();
wrapper.orderByDesc(User::getCreateTime)
.orderByAsc(User::getId); // 必加!避免 ROWNUM 分配不稳定</user>
- 只写
orderByDesc(User::getCreateTime)是常见坑点 - 即使表有主键,也必须显式加入
orderByAsc或orderByDesc,不能依赖默认 - 若用 XML 自定义 SQL,同样要在
ORDER BY后补上id或其他唯一列
Page 对象构造时 current 从 1 开始,且不能为 0 或负数
Oracle 分页对页码敏感:若传 new Page(0, 10),MyBatis-Plus 会生成 ROWNUM 这类非法条件,查不到数据也不报错,静默失败。
安全写法:
long current = Math.max(1, req.getPageNum()); // 强制兜底 long size = Math.min(500, req.getPageSize()); // 防超限 Page<user> page = new Page(current, size);</user>
-
current小于 1 会导致ROWNUM ,结果为空 -
size过大会触发maxLimit限制(默认无上限,但建议手动设为 500) - 前端传参异常(如
page=abc)需在 Controller 层校验,不能全交给 MyBatis-Plus
自定义 SQL 分页需手动适配 Oracle,selectPage 不生效
当你在 Mapper XML 中写了 ,哪怕方法签名是 Page,MyBatis-Plus 的拦截器也不会重写你的 SQL —— 它只处理 BaseMapper 提供的 selectPage 方法或注解 SQL。
此时必须自己写 Oracle 兼容分页:
<select id="selectUserPage" resulttype="User">
SELECT * FROM (
SELECT TMP.*, ROWNUM RN FROM (
SELECT u.* FROM user u
<where><if test="name != null and name != ''">
AND u.name LIKE CONCAT('%', #{name}, '%')
</if></where>
ORDER BY u.create_time DESC, u.id ASC -- 同样要加唯一排序
) TMP
WHERE ROWNUM #{page.size} * (#{page.current} - 1)
</select>
- XML 中无法直接用
page.current和page.size,必须通过#{page.xxx}访问 - 嵌套两层子查询是 Oracle 分页标准写法,少一层会出错
- 这种写法绕过了 MyBatis-Plus 分页插件,
total字段不会自动填充,需额外查一次 COUNT
Oracle 分页最易被忽略的其实是排序稳定性 —— 看似只是加个 ORDER BY,但缺了唯一字段,数据就可能在不同页间漂移,而且这种问题在线上低概率出现,极难复现和定位。











