Java调用Oracle存储过程返回REF CURSOR时,必须用CallableStatement.registerOutParameter(index, Types.OTHER)显式注册,执行后调用getResultSet()获取结果集,不可用getObject()或getString();存储过程输出参数类型须为SYS_REFCURSOR,且驱动需兼容JDK 17与数据库版本。

Oracle存储过程返回REF CURSOR,Java要用CallableStatement注册Types.OTHER
Java 17 本身不改变 JDBC 调用游标的方式,但必须确保使用支持 Oracle 21c/19c 的 JDBC 驱动(如 ojdbc11 或 ojdbc10),且驱动版本与 JDK 17 兼容。关键不是 Java 版本,而是 JDBC 规范对 REF CURSOR 的处理逻辑没变:它本质是服务器端游标句柄,JDBC 层必须显式声明为 Types.OTHER,再通过 ResultSet 获取。
常见错误是直接用 registerOutParameter(1, Types.INTEGER) 或漏掉 registerOutParameter,导致 SQLException: Invalid column type 或空结果。
- 存储过程中输出参数类型必须是
SYS_REFCURSOR(或自定义包中定义的REF CURSOR类型) - Java 中必须调用
registerOutParameter(index, Types.OTHER),不能用Types.STRUCT、Types.ARRAY或其他类型 - 调用
execute()后,用getResultSet()获取结果集 —— 不是getObject()或getString() - 若存储过程有多个 OUT 参数,
REF CURSOR必须是最后一个注册的,否则某些驱动版本会报错
示例:带输入参数和 REF CURSOR 输出的完整调用
假设 Oracle 存储过程定义如下:
CREATE OR REPLACE PROCEDURE get_user_by_dept(
p_dept_id IN NUMBER,
p_cursor OUT SYS_REFCURSOR
) AS
BEGIN
OPEN p_cursor FOR
SELECT id, name, email FROM users WHERE dept_id = p_dept_id;
END;对应 Java 调用(使用 try-with-resources 自动关闭):
立即学习“Java免费学习笔记(深入)”;
String sql = "{call get_user_by_dept(?, ?)}";
try (Connection conn = dataSource.getConnection();
CallableStatement cs = conn.prepareCall(sql)) {
<pre class='brush:php;toolbar:false;'>cs.setInt(1, 101); // 输入参数
cs.registerOutParameter(2, Types.OTHER); // 关键:注册为 OTHER
cs.execute();
try (ResultSet rs = cs.getResultSet()) { // 必须用 getResultSet()
while (rs.next()) {
String name = rs.getString("name");
String email = rs.getString("email");
System.out.println(name + " - " + email);
}
}}
为什么不用 setObject(2, null, Types.OTHER)?
有人尝试用 setObject 模拟 OUT 参数传入 null,这是无效的。Oracle 的 REF CURSOR 是纯输出参数,JDBC 要求必须用 registerOutParameter 显式声明其方向和类型,否则驱动无法在协议层预留游标接收位置。
-
setObject(...)仅适用于 IN 参数,对 OUT/INOUT 无效 - 即使语法不报错,运行时
getResultSet()会返回null - 某些旧版 ojdbc(如 ojdbc8 早期小版本)在未注册时还会抛
ORA-01008: not all variables bound
遇到 ORA-00900: invalid SQL statement 或连接中断?检查驱动和 URL
这类错误往往和 JDBC URL 格式或驱动能力有关,尤其在 Java 17 下默认启用 TLS 1.3、禁用旧加密套件后:
- 确保 URL 包含
?oracle.jdbc.javaNetNio=true(可选,但高并发下推荐) - 避免使用过时的
oracle.jdbc.driver.OracleDriver(已废弃),改用oracle.jdbc.OracleDriver - 若用 Oracle RAC 或服务名方式连接,URL 中的 service_name 必须正确,否则游标打开阶段就失败
- 如果存储过程里用了
DBMS_OUTPUT.PUT_LINE,别忘了在 Java 中调用cs.execute()后立刻执行conn.prepareStatement("BEGIN DBMS_OUTPUT.ENABLE(NULL); END;").execute(),否则看不到调试输出
真正容易被忽略的是:Oracle 游标生命周期绑定在数据库会话上,Java 端必须在同一个 Connection 上调用 getResultSet();跨 connection 或 connection pool 归还后再取,会报 ORA-01001: invalid cursor。


















