从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;
Run Code Online (Sandbox Code Playgroud)
要获得以下结果:
ResultSet #1
A
---------------------------------------
1
ResultSet #2
B
---------------------------------------
2
Run Code Online (Sandbox Code Playgroud)
这几乎像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;
}
}
Run Code Online (Sandbox Code Playgroud)
这导致ojdbc6版本12.1.0.1.0出错:
true
java.sql.SQLException: Oracle.main
上的oracle.jdbc.driver.OracleStatementWrapper.getResultSet(OracleStatementWrapper.java:388)
中的oracle.jdbc.driver.OracleStatement.getResultSet(OracleStatement.java:3369)
中没有可用的结果集(Oracle的.java:46)
这似乎违反了该Statement.execute()方法,其Javadoc指出:
返回:
如果第一个结果是ResultSet对象,则返回 true; 如果是更新计数或没有结果,则返回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;
}
}
Run Code Online (Sandbox Code Playgroud)
结果现在是:
true
获取结果0
true
获取结果1
false
但这似乎是错误的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;
}
}
Run Code Online (Sandbox Code Playgroud)
不提取任何结果集但只是退出:
假
它再次以这种方式工作:
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;
}
}
Run Code Online (Sandbox Code Playgroud)
true
获取结果0
true
获取结果1
false
假设我正在编写通用JDBC客户端代码,它不知道SQL字符串包含什么(它可能只是普通的查询).我想获取我可能得到的所有结果集.
需要明确的是,上述解决方法对于普通查询来说是错误的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;
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1271 次 |
| 最近记录: |