mybatis在oracle 10g/11gr1分页报错的根本原因是rownum嵌套层级超出解析器支持,需改用单层子查询+where rn between;xml中须用cdata或</>避免解析失败;动态order by必须白名单校验后用${}拼接。
mybatis 生成的 oracle 分页 sql 在老版本 oracle(如 10g 或早期 11g)上直接报错,根本原因不是语法写错了,而是默认分页逻辑依赖了 rownum 嵌套层级和别名可见性规则——这些在 oracle 11gr2 之后才逐步稳定,老库对子查询中 rownum 的处理更严格,稍有嵌套就触发 ora-00904: "rn": invalid identifier 或 ora-00936: missing expression。
Oracle 10g/11gR1 下 ROWNUM 嵌套不被允许的典型错误
MyBatis(尤其是 PageHelper 或手写分页)常生成类似这样的结构:
SELECT * FROM (
SELECT A.*, ROWNUM rn FROM (
SELECT * FROM user_table ORDER BY id
) A WHERE ROWNUM = 11
问题出在最外层 WHERE rn >= 11:Oracle 10g 不允许在外部查询中直接引用内层子查询定义的别名 rn,因为 ROWNUM 是在结果集生成时动态赋值的,且老版本解析器不支持跨两层子查询暴露该别名。
- 错误现象:
ORA-00904: "RN": invalid identifier - 真实执行时,Oracle 把第二层子查询(含
ROWNUM rn)当作“不可见列来源”,外层无法识别rn - 即使把
rn改成"rn"或加表别名A.rn,依然报错——这不是引号或别名问题,是解析器能力限制
兼容老 Oracle 的分页写法必须压平到单层 ROWNUM
绕过嵌套限制的唯一可靠方式,是把排序、行号、范围过滤全部塞进一个子查询里,让 ROWNUM 在同一作用域内完成判断:
SELECT * FROM (
SELECT B.*, ROWNUM rn FROM (
SELECT * FROM user_table ORDER BY id
) B
) WHERE rn BETWEEN 11 AND 20
这个写法在 Oracle 10g 上能跑通,关键点在于:
- 只有一层子查询包裹排序结果,
ROWNUM在该层直接附加,别名rn对外层WHERE可见 - 用
BETWEEN替代>=+组合,避免 XML 中 <code>/<code>>被误解析(尤其在 MyBatis XML mapper 里) - 不能省略中间别名
B,否则 Oracle 10g 会报ORA-00937: not a single-group group function
MyBatis XML 中写分页要避开 字符实体
在 mapper.xml 里硬写 ROWNUM 会导致 XML 解析失败,报错信息通常是:
Element content is not allowed in prolog 或 The content of elements must consist of well-formed character data or markup
这不是 SQL 错,是 XML 语法崩了。解决方案只有两个:
- 用 CDATA 包裹整段 SQL:
- 或改用字符实体:
<替代,<code>>替代>(注意是<,不是)
别用 直接写——哪怕它看起来像 HTML 实体,XML parser 不认;也别指望 MyBatis 自动转义,它只处理参数占位符,不管 SQL 字面量。
ORDER BY 动态字段必须用 ${},但得自己防 SQL 注入
如果分页 SQL 里 ORDER BY 的字段名来自参数(比如前端传 sortField=created_time),用 #{sortField} 会生成带引号的字符串字面量,导致 Oracle 报 ORA-00904: invalid identifier:
错误生成:ORDER BY 'created_time' DESC → 字段名变字符串,无效
正确做法是用 ${sortField} 让 MyBatis 直接拼入,但必须确保该参数值是白名单内的固定字段:
- 定义白名单:
Set<string> ALLOWED_SORT_FIELDS = Set.of("id", "name", "created_time", "status")</string> - 在 service 层校验:
if (!ALLOWED_SORT_FIELDS.contains(sortField)) throw new IllegalArgumentException("Invalid sort field") - XML 中写:
ORDER BY ${sortField} ${sortOrder}(sortOrder同样需校验为ASC/DESC)
跳过校验直接用 ${},等于给 SQL 注入开后门——老 Oracle 本身没新特性防护,这一环漏掉,比语法错误更致命。
老 Oracle 分页不是单纯换写法的问题,它逼你同时处理 XML 解析、SQL 作用域、动态拼接安全三重约束。少踩任意一个坑,都会在日志里看到一串 ORA- 开头的红字。











