Sqlserver
 sql >> Datenbank >  >> RDS >> Sqlserver

Wie man mit JDBC *alles* von einer gespeicherten Prozedur zurückbekommt

Wenn wir eine gespeicherte Prozedur in JDBC ausführen, erhalten wir eine Reihe von null oder mehr "Ergebnissen". Wir können diese "Ergebnisse" dann nacheinander verarbeiten, indem wir CallableStatement#getMoreResults() aufrufen . Jedes "Ergebnis" kann

enthalten
  • Null oder mehr Datenzeilen, die wir mit einem ResultSet abrufen können Objekt,
  • eine Aktualisierungsanzahl für eine DML-Anweisung (INSERT, UPDATE, DELETE), die wir mit CallableStatement#getUpdateCount() abrufen können , oder
  • ein Fehler, der eine SQLServerException auslöst.

Bei "Problem 1" besteht das Problem häufig darin, dass die gespeicherte Prozedur nicht mit SET NOCOUNT ON; beginnt und führt eine DML-Anweisung aus, bevor ein SELECT ausgeführt wird, um eine Ergebnismenge zu erzeugen. Der Aktualisierungszähler für die DML wird als erstes „Ergebnis“ zurückgegeben, und die Datenzeilen bleiben „dahinter stecken“, bis wir getMoreResults aufrufen .

"Ausgabe 2" ist im Wesentlichen das gleiche Problem. Die gespeicherte Prozedur erzeugt ein "Ergebnis" (normalerweise ein SELECT oder möglicherweise eine Aktualisierungszählung), bevor der Fehler auftritt. Der Fehler wird in einem nachfolgenden "Ergebnis" zurückgegeben und verursacht keine Ausnahme, bis wir ihn mit getMoreResults "abrufen". .

In vielen Fällen kann das Problem umgangen werden, indem einfach SET NOCOUNT ON; hinzugefügt wird als erste ausführbare Anweisung in der gespeicherten Prozedur. Allerdings ist eine Änderung der gespeicherten Prozedur nicht immer möglich und es bleibt dabei, dass man alles bekommt Zurück von der gespeicherten Prozedur müssen wir weiterhin getMoreResults aufrufen bis, wie das Javadoc sagt:

There are no more results when the following is true: 

     // stmt is a Statement object
     ((stmt.getMoreResults() == false) && (stmt.getUpdateCount() == -1))

Das hört sich einfach an, aber wie immer steckt „der Teufel im Detail“, wie das folgende Beispiel verdeutlicht. Für eine gespeicherte SQL Server-Prozedur ...

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

... der folgende Java-Code gibt alles zurück ...

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]");
}

... erzeugt die folgende Konsolenausgabe:

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]