必须配置Oracle12cDialect,否则Hibernate会fallback至ROWNUM嵌套写法,引发ORA-00933;该方言是启用OFFSET/FETCH标准分页语法的唯一可靠配置,且需在application.yml中显式声明database-platform、database及hibernate.dialect三者。
必须配置 oracle12cdialect,否则分页 sql 会生成 offset ? rows fetch next ? rows only —— oracle 12c 虽支持该语法,但 spring boot 2.x 默认的 oracle10gdialect 或 oracle12cdialect 未正确启用分页支持时,仍可能 fallback 到旧式 rownum 嵌套写法,导致 ora-00933。
为什么 Oracle12cDialect 是硬性要求
Spring Data JPA 的分页依赖 Hibernate 自动生成分页子句。Oracle 12c 引入了标准 SQL:2008 分页语法(OFFSET ... FETCH),但 Hibernate 必须明确知道目标数据库版本才能启用它。若方言配置为 Oracle10gDialect,Hibernate 会强行使用 ROWNUM 嵌套查询(如 SELECT * FROM (SELECT ..., ROWNUM rn FROM (...)) WHERE rn BETWEEN ? AND ?),这种写法在复杂 ORDER BY 或含 WITH 子句的场景下极易因括号/别名/分号位置出错,直接触发 ORA-00933: SQL 命令未正确结束。
而 Oracle12cDialect(注意不是 OracleDialect)才真正启用了 OFFSET/FETCH 支持,并能正确处理 Pageable 中的排序、偏移与限制参数。
- Spring Boot 2.0+ 默认不自动推断 Oracle 版本,必须显式指定方言
-
spring.jpa.database-platform=org.hibernate.dialect.Oracle12cDialect是唯一可靠配置项 - 若项目同时兼容 SQLite,切勿保留 SQLite 的分页配置(如
spring.jpa.database=SQLITE或自定义Dialect),它们会干扰 Oracle 的方言解析
application.yml 中必须写的三行配置
仅加 database-platform 不够,还需关闭 Hibernate 自动检测和方言覆盖风险:
spring.jpa.database-platform=org.hibernate.dialect.Oracle12cDialect-
spring.jpa.database=ORACLE(显式声明,避免 Hibernate 尝试 auto-detect) -
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.Oracle12cDialect(双重保险,防止某些版本忽略 platform 配置)
漏掉任意一行,在多数据源或混合环境(如测试用 H2、生产用 Oracle)中都可能 fallback 到错误方言。
@Query 原生 SQL 分页必须手动注入 /*#{pageable}*/
即使方言正确,自定义 @Query(nativeQuery = true) 也不会自动拼接分页子句 —— Hibernate 只对 JPQL 查询做自动分页增强。原生 SQL 需显式占位:
@Query(value = "SELECT * FROM users WHERE status = ?1 ORDER BY created_at DESC /*#{pageable}*/",
countQuery = "SELECT COUNT(*) FROM users WHERE status = ?1",
nativeQuery = true)
Page<User> findByStatus(String status, Pageable pageable);关键点:
-
/*#{pageable}*/必须写在ORDER BY后、分号前,且不能换行 -
countQuery不能含分页逻辑,也不能引用pageable参数,否则抛Could not locate ordinal parameter - 若主查询含
GROUP BY或UNION,OFFSET/FETCH可能被 Oracle 拒绝,此时需改用ROWNUM手写分页或拆成两步查
PageRequest 已废弃,必须用 Pageable.of()
Spring Data JPA 2.0+ 中 PageRequest 构造器已标记为 @Deprecated,直接 new PageRequest(0, 10) 会导致启动失败或运行时报 Paging query needs to have a Pageable parameter!:
- ✅ 正确写法:
Pageable pageable = PageRequest.of(0, 10, Sort.by("id").ascending());(注意是PageRequest.of()静态工厂方法) - ✅ 更推荐:
Pageable pageable = Pageable.unpaged();或Pageable.ofSize(10).withPage(0) - ❌ 错误写法:
new PageRequest(0, 10)(构造器已删)、new PageRequest(0, 10, Sort.Direction.ASC, "id")
这个细节常被忽略,尤其当从 Spring Boot 1.x 升级时,旧代码不改就会卡在 Bean 创建阶段。
最易被忽略的是:方言配置必须与 Oracle 实际版本严格匹配 —— 用 12c 的库连 19c 数据库,或反过来,都可能因 OFFSET/FETCH 语义差异导致分页结果错乱,而非报错。真要跨版本,宁可降级用 Oracle12cDialect + 显式 ROWNUM 手写分页,也别信“自动适配”。

















