ORA-22920 错误源于未加 FOR UPDATE 就写 CLOB,必须在事务中显式锁定行;DBMS_LOB.WRITEAPPEND 单次限 32767 字节需分段;临时 CLOB 必须显式 FREETEMPORARY;Java 应用需用 setClob(Reader) 避免隐式降级。
ORA-22920:没加 FOR UPDATE 就写 CLOB?
直接对 clob 字段执行 update 或在 pl/sql 中用 dbms_lob.writeappend,却没先锁定行,就会报这个错。oracle 要求:任何写入 clob 的操作,必须在事务内、对目标行显式加锁。
正确做法是:SELECT clob_col INTO :lob_var FROM t WHERE id = :id FOR UPDATE。注意三点:
-
FOR UPDATE不能省略,也不能写成FOR UPDATE NOWAIT(除非你明确要非阻塞) - 必须在同一个事务中完成
SELECT ... FOR UPDATE和后续的DBMS_LOB操作 - 如果表有分区或使用了物化视图,
FOR UPDATE可能不生效,需确认执行计划是否真的命中目标行
DBMS_LOB.WRITEAPPEND 分段写入必须控制长度
DBMS_LOB.WRITEAPPEND 每次最多接受 VARCHAR2(32767) 字节,超长会直接报 ORA-06502。这不是缓冲区大小问题,是 Oracle 内部限制。
实际写入逻辑要拆成循环:
- 用
DBMS_LOB.GETLENGTH获取源字符串总长 - 每次取
SUBSTR(src, offset, 32767)截断,再传给WRITEAPPEND - offset 每次递增 32767,直到处理完
- 别用
DBMS_LOB.WRITE替代——它要求指定起始位置,容易覆盖已有内容
临时 CLOB 忘记 FREETEMPORARY?PGA 内存就 leak 了
用 DBMS_LOB.CREATETEMPORARY 创建的 CLOB 不是自动回收的。尤其在存储过程中高频调用时,PGA 内存会持续上涨,最终可能触发 ORA-04030。
关键点:
- 必须设
cache => TRUE,否则后续DBMS_LOB.READ或GETLENGTH返回 0 - 写完后必须配对调用
DBMS_LOB.FREETEMPORARY,哪怕过程异常退出也要在EXCEPTION块里补上 - 不能依赖会话结束自动释放——临时 LOB 生命周期由显式调用控制
Java 应用层传 CLOB 到存储过程,别用 setString()
JDBC 驱动对 setString() 处理 CLOB 参数是“隐式降级”:超过 4000 字节就转成临时 CLOB,但不走流式通道,仍可能触发 ORA-22922 或 OOM。
安全做法是:
- 用
setClob(int, Reader),传new StringReader(content)(小文本)或Files.newBufferedReader(path)(大文件) - 避免
getClob().getSubString()回传——这一步已全量加载进 JVM 堆 - 如果调用的是含
IN OUT CLOB参数的过程,确保 Java 端也用registerOutParameter(idx, Types.CLOB)显式声明
CLOB 的坑不在容量上限,而在生命周期管理与上下文绑定。每个 DBMS_LOB 调用都依赖前序状态(是否锁定、是否 cache、是否临时),漏掉任意一环,错误就不是“写不进去”,而是“写进去但读不出来”或“内存悄悄涨”。











