从JDBC查询中获取所有(可能是隐式)结果的正确方法是什么?

从Oracle 12c开始,我们可以从客户端获取隐式游标。 例如,可以在SQL Developer中运行以下PL / SQL匿名块

DECLARE c1 sys_refcursor; c2 sys_refcursor; BEGIN OPEN c1 FOR SELECT 1 AS a FROM dual; dbms_sql.return_result(c1); OPEN c2 FOR SELECT 2 AS b FROM dual; dbms_sql.return_result(c2); END; 

要获得以下结果:

 ResultSet #1 A --------------------------------------- 1 ResultSet #2 B --------------------------------------- 2 

这几乎像MySQL或SQL Server批处理( 例如,如本文所示 ),所以我认为我们应该能够运行以下代码:

 try (Statement s = connection.createStatement()) { boolean result = s.execute(sql); // Plug above SQL here fetchLoop: for (int i = 0;; i++) { if (i > 0) result = s.getMoreResults(); System.out.println(result); if (result) try (ResultSet rs = s.getResultSet()) { System.out.println("Fetching result " + i); // ... } else if (s.getUpdateCount() == -1) break fetchLoop; } } 

这导致ojdbc6版本12.1.0.1.0出错:

真正
java.sql.SQLException:没有可用的结果集
at oracle.jdbc.driver.OracleStatement.getResultSet(OracleStatement.java:3369)
at oracle.jdbc.driver.OracleStatementWrapper.getResultSet(OracleStatementWrapper.java:388)
在Oracle.main(Oracle.java:46)

这似乎违反了Statement.execute()方法,其Javadoc指示:

返回:
如果第一个结果是ResultSet对象,则为true;否则为false。 如果是更新计数或没有结果,则返回false

因此,一旦Statement.execute()产生true ,我认为Statement.getResultSet()应该返回一个结果集。 解决方法是:

 try (Statement s = connection.createStatement()) { s.execute(sql); // WORKAROUND: Ignore this result fetchLoop: for (int i = 0;; i++) { boolean result = s.getMoreResults(); // WORKAROUND: Take the result from here System.out.println(result); if (result) try (ResultSet rs = s.getResultSet()) { System.out.println("Fetching result " + i); // ... } else if (s.getUpdateCount() == -1) break fetchLoop; } } 

结果现在是:

真正
获取结果0
真正
获取结果1

但这似乎是错误的API使用。 根据我对JDBC规范的理解,这个“改进的”循环现在将跳过第一个结果集。

更糟糕的是,准备好的陈述表现不同。 以下代码:

 try (PreparedStatement s = cn.prepareStatement(sql)) { boolean result = s.execute(); fetchLoop: for (int i = 0;; i++) { if (i > 0) result = s.getMoreResults(); System.out.println(result); if (result) try (ResultSet rs = s.getResultSet()) { System.out.println("Fetching result " + i); // ... } else if (s.getUpdateCount() == -1) break fetchLoop; } } 

不提取任何结果集但只是退出:

它再次以这种方式工作:

 try (PreparedStatement s = cn.prepareStatement(sql)) { s.execute(); fetchLoop: for (int i = 0;; i++) { boolean result = s.getMoreResults(); System.out.println(result); if (result) try (ResultSet rs = s.getResultSet()) { System.out.println("Fetching result " + i); // ... } else if (s.getUpdateCount() == -1) break fetchLoop; } } 

真正
获取结果0
真正
获取结果1

我的问题(最后)

假设我正在编写通用JDBC客户端代码,它不知道SQL字符串包含什么(它可能只是一个普通的查询)。 我想获取我可能获得的所有结果集。

  • ojdbc是否违反了JDBC规范?
  • 如果SQL字符串未知,从ojdbc获取所有结果集的正确方法是什么?

需要说明的是,对于SELECT 1 FROM dual这样的普通查询,上述解决方法是错误的。

暂时(虽然这可能是也可能不是ojdbc中的错误),我发现这个讨厌的解决方法涵盖了JDBC API的所有用法(不知道SQL字符串产生了什么):

 /* Alternatively, use this for non-PreparedStatements: try (Statement s = cn.createStatement()) { Boolean result = s.execute(sql); */ try (PreparedStatement s = cn.prepareStatement(sql)) { // Use good old three-valued boolean logic Boolean result = s.execute(); fetchLoop: for (int i = 0;; i++) { // Check for more results if not already done in this iteration if (i > 0 && result == null) result = s.getMoreResults(); System.out.println(result); if (result) { result = null; try (ResultSet rs = s.getResultSet()) { System.out.println("Fetching result " + i); } catch (SQLException e) { // Ignore ORA-17283: No resultset available if (e.getErrorCode() == 17283) continue fetchLoop; else throw e; } } else if (s.getUpdateCount() == -1) // Ignore -1 value if there is one more result! if (result = s.getMoreResults()) continue fetchLoop; else break fetchLoop; } }