ORA-01000根因是Java代码未显式关闭PreparedStatement/ResultSet,连接池close()仅归还连接而不销毁Oracle会话,游标持续累积;必须用try-with-resources按ResultSet→Statement→Connection逆序关闭,禁用语句缓存,改单条执行为addBatch(),调大open_cursors仅为最后手段。

ORA-01000 不是数据库配小了,而是 Java 代码里 PreparedStatement 或 ResultSet 没关,连接池把“挂着游标的连接”反复借出去,越积越多,300 个上限一到就崩。
为什么 conn.close() 根本不释放游标
连接池(比如 HikariCP、Druid)的 close() 只是归还物理连接,不是销毁 Oracle 会话。游标是会话级资源,由 PreparedStatement 和 ResultSet 创建并持有,只要它们没显式 close(),游标就一直留在 v$open_cursor 里不释放。
- 错误写法:
stmt = conn.createStatement()在 for 循环里反复 new,只在 finally 里stmt.close()一次 → 实际只关了最后一个 - 更隐蔽的坑:用了语句缓存(
statementCacheSize > 0),调用ps.close()其实只是放回缓存,服务端游标根本没释放 - 查证方式:DBA 执行
SELECT COUNT(*) FROM v$open_cursor WHERE sid = ?,看单个会话游标数是否随请求持续上涨
必须用 try-with-resources 包三层资源
只包 Connection 是无效的;必须让 Connection、PreparedStatement、ResultSet 各自独立出现在 try 括号中,才能按 ResultSet → PreparedStatement → Connection 逆序自动关闭。
try (Connection conn = dataSource.getConnection();
PreparedStatement ps = conn.prepareStatement("SELECT * FROM t WHERE id = ?");
ResultSet rs = ps.executeQuery()) {
while (rs.next()) { /* 处理 */ }
} // 三者在此处依次 close,游标真正释放- 不能复用同一个
PreparedStatement实例去执行不同 SQL —— 参数类型或数量变化会触发隐式游标残留 - 如果 SQL 动态拼接,别用
ps复用,老老实实每次 new + try-with-resources - 批量操作别写 for 循环 + 单条
executeUpdate(),改用addBatch()+executeBatch()
禁用语句缓存快速验证是不是它惹的祸
很多驱动和连接池默认开启语句缓存(WebLogic 默认 200,Oracle UCP 也常开),这是 ORA-01000 的高发区。临时禁用后若报错消失,就坐实是缓存配置过大。
立即学习“Java免费学习笔记(深入)”;
- JDBC URL 加参数:
;statementCacheSize=0 - HikariCP 用户在
application.yml中配:spring.datasource.hikari.data-source-properties.statementCacheSize: 0 - 生产环境合理值通常是 20~50,别保留默认 200
- 禁用后问题仍存在?说明泄漏在代码里,不是缓存机制导致的
调大 open_cursors 是最后手段,且掩盖问题
open_cursors 是会话级软限制,默认常为 300。DBA 执行 ALTER SYSTEM SET open_cursors = 1000 SCOPE=BOTH 能热生效,但只是把爆炸时间往后推——只要代码里还有漏关的 PreparedStatement,游标照样累积,迟早再爆。
- 盲目设成 5000 不解决泄漏,反而让问题更难定位
- 调参前务必确认:已禁用语句缓存 + 已修复所有未关闭资源的代码路径 + 已用
addBatch()替代循环单条执行 - 真正复杂的是异常分支:比如
rs.next()过程中抛 NPE,rs和ps都没走到 close —— 这正是 try-with-resources 要解决的核心场景


















