ORA-01000根源是Statement/ResultSet未显式关闭导致Oracle会话游标泄漏,Connection.close()仅归还连接而不释放游标;必须用try-with-resources按ResultSet→Statement→Connection顺序关闭,禁用语句缓存并修复代码而非调大open_cursors。

必须显式关闭 Statement 和 ResultSet,Connection.close() 不会释放 Oracle 游标。 这是绝大多数 ORA-01000 报错的根源——不是数据库配小了,而是 Java 代码漏关资源,尤其在连接池场景下,游标会持续累积直到爆限。
为什么 close(Connection) 不等于关游标
连接池(如 HikariCP、Druid)的 close() 只是把物理连接归还给池,Oracle 数据库端的会话依然存活。每个 Statement(含 PreparedStatement、CallableStatement)都会在该会话中独占一个服务器游标。只要没调 stmt.close(),游标就一直挂着。
- Oracle 的
open_cursors参数默认常为 300 或 500,远低于连接池常见并发数 × 每连接执行语句数 - 查证方式:用 DBA 账号执行
SELECT COUNT(*) FROM v$open_cursor WHERE sid = ?,看单个会话游标数是否随请求增长 - 即使用了
try-with-resources,若Statement是在方法外创建、传入再执行,资源管理也会脱节
CallableStatement 调用带游标的存储过程时怎么关
调用 PROCEDURE proc_list(p_id IN NUMBER, p_out OUT refcur) 类型的包过程时,游标泄漏风险极高——因为 ResultSet 是从 getObject(2) 或 getCursor(2) 拿出来的,容易被当成普通结果集忽略关闭。
当用户要求为 Java 代码添加注释、修改注释、审查注释、整理注释格式,或要求按照代码注释规范处理 Java 文件时,使用本技能。本技能用于统一生成和维护 Java 类注释模板、按 Git 对比 release 判断版本号、补充每行代码上方中文块注释、调整注释空行规则,并保持 Java 文件 UTF-8 无 BOM 编码。典型触发场景包括:补充类注释、更新 @version、检查 @author 来源、整理行尾注释、把注释放到代码上一行、检查注释是否符合规范、构建前检查 Java 注释风格。
- 必须注册
oracle.jdbc.OracleTypes.CURSOR,不能用java.sql.Types.OTHER -
ResultSet rs = (ResultSet) cs.getObject(2)后,必须显式 close;推荐用try (ResultSet rs = (ResultSet) cs.getObject(2)) { ... } - 不要在存储过程中对
p_out执行CLOSE,JDBC 会接管生命周期,手动关反而报错 - 游标绑定的是底层会话资源,漏关会导致连接池中该连接后续请求卡死或直接触发 ORA-01000
流式读 CLOB 时游标和 LOB 句柄怎么一并释放
读取 CLOB 字段时不关流,不仅会 OOM,还会导致 LOB 句柄和关联游标双重泄漏——因为 Oracle 驱动内部用游标维护 LOB locator 状态。
立即学习“Java免费学习笔记(深入)”;
- 禁用
getClob().getSubString()和toString(),它们强制全量加载进堆内存 - 改用
rs.getCharacterStream("col_name"),并严格包裹在try-with-resources中 - 缓冲区大小设
8192足够;避免BufferedReader.readLine(),它内部缓冲不可控 - 注意:
Reader生命期绑定当前ResultSet行,调过一次rs.next()后,前一行的流立即失效,再读抛SQLException: Stream has already been closed
最容易被忽略的是:游标泄漏不只发生在 SQL 执行层,也藏在 LOB 流、存储过程游标、甚至语句缓存开关里。哪怕你调了 stmt.close(),如果驱动启用了 statementCacheSize > 0,close 实际只是放回缓存,游标仍不释放——这时候得关掉缓存,或确保缓存策略与游标生命周期对齐。

















