Когда мы выполняем хранимую процедуру в JDBC, мы возвращаем серию нулевых или более «результатов». Затем мы можем обрабатывать эти «результаты» последовательно, вызывая CallableStatement#getMoreResults()
. Каждый «результат» может содержать
- строк данных, которые мы можем получить с
ResultSet
объекта,
- счетчика изменений для заявления DML (INSERT, UPDATE, DELETE), которые мы можем получить с
CallableStatement#getUpdateCount()
или
- Ошибка, которая генерирует исключение SQLServerException.
Для «Выпуск 1» проблемы часто, что хранимая процедура не начинается с SET NOCOUNT ON;
и выполняет оператор DML перед выполнением SELECT, чтобы произвести набор результатов. Количество обновлений для DML возвращается как первый «результат», а строки данных «застряли за ним», пока мы не позвоним getMoreResults
.
«Проблема 2» по существу такая же проблема. Хранимая процедура создает «результат» (обычно SELECT или, возможно, счетчик обновлений) до возникновения ошибки. Ошибка возвращается в следующем «результате» и не вызывает исключения до тех пор, пока мы «не извлекли» его, используя getMoreResults
.
Во многих случаях проблему можно избежать, просто добавив SET NOCOUNT ON;
в качестве первого исполняемого оператора в хранимой процедуре. Однако изменение хранимой процедуры не всегда возможно, и факт остается фактом, что для того, чтобы получить все назад от хранимой процедуры мы должны держать называя getMoreResults
пока, как Javadoc говорит:
There are no more results when the following is true:
// stmt is a Statement object
((stmt.getMoreResults() == false) && (stmt.getUpdateCount() == -1))
Это звучит достаточно просто, но, как обычно, «дьявол в деталях», как показано на следующем примере. Для SQL процедуры сервера хранятся ...
ALTER PROCEDURE dbo.TroublesomeSP AS
BEGIN
-- note: no `SET NOCOUNT ON;`
DECLARE @tbl TABLE (id VARCHAR(3) PRIMARY KEY);
DROP TABLE NonExistent;
INSERT INTO @tbl (id) VALUES ('001');
SELECT id FROM @tbl;
INSERT INTO @tbl (id) VALUES ('001'); -- duplicate key error
SELECT 1/0; -- error _inside_ ResultSet
INSERT INTO @tbl (id) VALUES ('101');
INSERT INTO @tbl (id) VALUES ('201'),('202');
SELECT id FROM @tbl;
END
... следующий код Java будет все вернуть ...
try (CallableStatement cs = conn.prepareCall("{call dbo.TroublesomeSP}")) {
boolean resultSetAvailable = false;
int numberOfResultsProcessed = 0;
try {
resultSetAvailable = cs.execute();
} catch (SQLServerException sse) {
System.out.printf("Exception thrown on execute: %s%n%n", sse.getMessage());
numberOfResultsProcessed++;
}
int updateCount = -2; // initialize to impossible(?) value
while (true) {
boolean exceptionOccurred = true;
do {
try {
if (numberOfResultsProcessed > 0) {
resultSetAvailable = cs.getMoreResults();
}
exceptionOccurred = false;
updateCount = cs.getUpdateCount();
} catch (SQLServerException sse) {
System.out.printf("Current result is an exception: %s%n%n", sse.getMessage());
}
numberOfResultsProcessed++;
} while (exceptionOccurred);
if ((!resultSetAvailable) && (updateCount == -1)) {
break; // we're done
}
if (resultSetAvailable) {
System.out.println("Current result is a ResultSet:");
try (ResultSet rs = cs.getResultSet()) {
try {
while (rs.next()) {
System.out.println(rs.getString(1));
}
} catch (SQLServerException sse) {
System.out.printf("Exception while processing ResultSet: %s%n", sse.getMessage());
}
}
} else {
System.out.printf("Current result is an update count: %d %s affected%n",
updateCount,
updateCount == 1 ? "row was" : "rows were");
}
System.out.println();
}
System.out.println("[end of results]");
}
...производя следующий вывод на консоль:
Exception thrown on execute: Cannot drop the table 'NonExistent', because it does not exist or you do not have permission.
Current result is an update count: 1 row was affected
Current result is a ResultSet:
001
Current result is an exception: Violation of PRIMARY KEY constraint 'PK__#314D4EA__3213E83F3335971A'. Cannot insert duplicate key in object '[email protected]'. The duplicate key value is (001).
Current result is a ResultSet:
Exception while processing ResultSet: Divide by zero error encountered.
Current result is an update count: 1 row was affected
Current result is an update count: 2 rows were affected
Current result is a ResultSet:
001
101
201
202
[end of results]
Связанный: http://stackoverflow.com/questions/14213824/java-sql-statement-hasresultset и http://stackoverflow.com/questions/24694442/expecting-multiple- resultsets-but-only-get-one и http://stackoverflow.com/questions/32561550/jdbc-sql-server-raced-error-handling-in-multi-statement-stored-procedures –