Når vi udfører en lagret procedure i JDBC, får vi en række nul eller flere "resultater" tilbage. Vi kan derefter behandle disse "resultater" sekventielt ved at kalde CallableStatement#getMoreResults()
. Hvert "resultat" kan indeholde
- nul eller flere rækker af data, som vi kan hente med et
ResultSet
objekt, - et antal opdateringer for en DML-sætning (INSERT, UPDATE, DELETE), som vi kan hente med
CallableStatement#getUpdateCount()
, eller - en fejl, der kaster en SQLServerException.
For "Udgave 1" er problemet ofte, at den lagrede procedure ikke begynder med SET NOCOUNT ON;
og udfører en DML-sætning, før du laver en SELECT for at producere et resultatsæt. Opdateringsantallet for DML returneres som det første "resultat", og datarækkerne sidder "fast bagved", indtil vi kalder getMoreResults
.
"Udgave 2" er stort set samme problem. Den lagrede procedure producerer et "resultat" (normalt et SELECT eller muligvis et opdateringstal), før fejlen opstår. Fejlen returneres i et efterfølgende "resultat" og forårsager ikke en undtagelse, før vi "henter" den ved hjælp af getMoreResults
.
I mange tilfælde kan problemet undgås ved blot at tilføje SET NOCOUNT ON;
som den første eksekverbare sætning i den lagrede procedure. Det er dog ikke altid muligt at ændre den lagrede procedure, og faktum er, at for at få alt tilbage fra den lagrede procedure er vi nødt til at blive ved med at kalde getMoreResults
indtil, som Javadoc siger:
There are no more results when the following is true:
// stmt is a Statement object
((stmt.getMoreResults() == false) && (stmt.getUpdateCount() == -1))
Det lyder simpelt nok, men som sædvanlig er "djævelen i detaljerne", som illustreret af følgende eksempel. For en SQL Server-lagret procedure ...
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
... den følgende Java-kode vil returnere alt ...
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]");
}
... producerer følgende konsoloutput:
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]