När vi kör en lagrad procedur i JDBC får vi tillbaka en serie med noll eller fler "resultat". Vi kan sedan behandla dessa "resultat" sekventiellt genom att anropa CallableStatement#getMoreResults()
. Varje "resultat" kan innehålla
- noll eller fler rader med data som vi kan hämta med en
ResultSet
objekt, - ett uppdateringsantal för en DML-sats (INSERT, UPDATE, DELETE) som vi kan hämta med
CallableStatement#getUpdateCount()
, eller - ett fel som skapar en SQLServerException.
För "Issue 1" är problemet ofta att den lagrade proceduren inte börjar med SET NOCOUNT ON;
och exekverar en DML-sats innan du gör en SELECT för att producera en resultatuppsättning. Uppdateringsantalet för DML returneras som det första "resultatet" och dataraderna "fastnar bakom det" tills vi anropar getMoreResults
.
"Problem 2" är i princip samma problem. Den lagrade proceduren producerar ett "resultat" (vanligtvis en SELECT, eller möjligen en uppdateringsräkning) innan felet inträffar. Felet returneras i ett efterföljande "resultat" och orsakar inget undantag förrän vi "hämtar" det med getMoreResults
.
I många fall kan problemet undvikas genom att helt enkelt lägga till SET NOCOUNT ON;
som den första körbara satsen i den lagrade proceduren. Men en ändring av den lagrade proceduren är inte alltid möjlig och faktum kvarstår att för att få allt tillbaka från den lagrade proceduren måste vi fortsätta anropa getMoreResults
tills, som Javadoc säger:
There are no more results when the following is true:
// stmt is a Statement object
((stmt.getMoreResults() == false) && (stmt.getUpdateCount() == -1))
Det låter enkelt nog, men som vanligt är "djävulen i detaljerna", vilket illustreras av följande exempel. För en lagrad SQL Server-procedur ...
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
... kommer följande Java-kod att returnera allt ...
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]");
}
... producerar följande konsolutgång:
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]