一聚教程网:一个值得你收藏的教程网站

最新下载

热门教程

如何在Java中读取Oracle SYS_REFCURSOR

时间:2026-08-19 09:56:48 编辑:袖梨 来源:一聚教程网

Oracle JDBC驱动需显式注册SYS_REFCURSOR为Types.OTHER,执行前注册、执行后取结果,用getObject()获取ResultSet并手动close(),否则引发ORA-01000等错误。

Oracle JDBC驱动必须启用REF CURSOR支持

默认情况下,OracleDriver 不会自动将 SYS_REFCURSOR 映射为 ResultSet,必须显式注册类型。不注册就调用 getResultSet() 会抛 SQLException: invalid column type 或返回 null

关键操作是:在 CallableStatement 执行前,对每个 SYS_REFCURSOR 输出参数调用 registerOutParameter(int paramIndex, Types.OTHER),且驱动版本需 ≥ 12.1(推荐使用 ojdbc8.jar)。

  1. 必须用 Types.OTHER,不能用 Types.STRUCTTypes.JAVA_OBJECT
  2. 若存储过程有多个 REF CURSOR,每个都要单独 registerOutParameter
  3. 注册顺序必须与 SQL 中 OUT 参数声明顺序一致

正确获取 ResultSet 的三步顺序不能颠倒

拿到 CallableStatement 后,执行顺序错一步就会失败:先注册 → 再执行 → 最后取结果。常见错误是执行完才去注册,或注册后又调用 setXXX() 改变参数状态。

String sql = "{call my_pkg.get_data(?, ?)}";CallableStatement cs = conn.prepareCall(sql);cs.registerOutParameter(2, Types.OTHER); // 第2个参数是 SYS_REFCURSORcs.setString(1, "key");cs.execute(); // 必须 execute() 之后才能 getResultSet()ResultSet rs = (ResultSet) cs.getObject(2); // 推荐用 getObject(),不是 getResultSet()
  1. cs.getObject(2)cs.getResultSet() 更可靠;后者仅适用于最后一个 OUT 参数是 REF CURSOR 的场景
  2. 执行前不能调用 cs.getResultSet(),会报 “statement not executed”
  3. 如果存储过程还返回其他标量值(如 INT),要在 execute() 后、取 ResultSet 前读取

ResultSet 关闭时机影响连接池行为

REF CURSOR 对应的 ResultSet 生命周期绑定到数据库游标,不及时关闭会导致 Oracle 端游标堆积、ORA-01000 错误,尤其在 HikariCP 或 Druid 连接池中表现明显。

  1. 必须在业务逻辑处理完后显式调用 rs.close(),不能依赖 try-with-resources 自动关闭 —— 因为 rs 是从 cs 拿出来的,不是直接创建的
  2. cs.close() 不会自动关闭其派生的 ResultSet,JDBC 规范明确要求手动关
  3. 若用 Spring JdbcTemplate,需配合 ConnectionCallback 手动管理,它不支持 REF CURSOR 自动释放

空结果集或 NULL REF CURSOR 的判断方式

存储过程可能因条件不满足返回 NULLSYS_REFCURSOR,此时 getObject(2) 返回 null,而非空 ResultSet。直接调用 next() 会 NPE。

Object obj = cs.getObject(2);if (obj == null) {// 存储过程未打开游标,按无数据处理} else {ResultSet rs = (ResultSet) obj;while (rs.next()) { /* ... */ }rs.close();}
  1. 不能用 rs == null 判断,要先检查 getObject() 返回值
  2. Oracle 侧若用 OPEN refcur FOR NULL;,Java 端收到的就是 null
  3. 某些旧版驱动在 NULL 游标下会抛 NullPointerException 而非返回 null,升级驱动可规避

实际中最容易被忽略的是:REF CURSOR 的生命周期完全由 Oracle 控制,Java 层没有任何缓冲或延迟释放机制。一旦忘记 close(),问题往往在压测时才暴露,且日志里只显示游标耗尽,很难关联到具体哪段代码漏关。

热门栏目