sql >> Database >  >> RDS >> Sqlserver

Hoe *alles* terug te krijgen van een opgeslagen procedure met JDBC

Wanneer we een opgeslagen procedure in JDBC uitvoeren, krijgen we een reeks van nul of meer "resultaten" terug. We kunnen die "resultaten" dan achtereenvolgens verwerken door CallableStatement#getMoreResults() aan te roepen. . Elk "resultaat" kan

. bevatten
  • nul of meer rijen met gegevens die we kunnen ophalen met een ResultSet voorwerp,
  • een updatetelling voor een DML-instructie (INSERT, UPDATE, DELETE) die we kunnen ophalen met CallableStatement#getUpdateCount() , of
  • een fout die een SQLServerException genereert.

Voor "Issue 1" is het probleem vaak dat de opgeslagen procedure niet begint met SET NOCOUNT ON; en voert een DML-instructie uit voordat een SELECT wordt uitgevoerd om een ​​resultatenset te produceren. Het aantal updates voor de DML wordt geretourneerd als het eerste "resultaat" en de gegevensrijen blijven "erachter hangen" totdat we getMoreResults aanroepen. .

"Probleem 2" is in wezen hetzelfde probleem. De opgeslagen procedure produceert een "resultaat" (meestal een SELECT, of mogelijk een updatetelling) voordat de fout optreedt. De fout wordt geretourneerd in een volgend "resultaat" en veroorzaakt geen uitzondering totdat we het "ophalen" met behulp van getMoreResults .

In veel gevallen kan het probleem worden vermeden door simpelweg SET NOCOUNT ON; . toe te voegen als de eerste uitvoerbare instructie in de opgeslagen procedure. Een wijziging van de opgeslagen procedure is echter niet altijd mogelijk en het feit blijft dat om alles te krijgen terug van de opgeslagen procedure moeten we getMoreResults blijven aanroepen totdat, zoals de Javadoc zegt:

There are no more results when the following is true: 

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

Dat klinkt eenvoudig genoeg, maar zoals gewoonlijk, "de duivel zit in de details", zoals geïllustreerd door het volgende voorbeeld. Voor een SQL Server-opgeslagen 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

... de volgende Java-code zal alles teruggeven ...

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

... de volgende console-uitvoer produceren:

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]



  1. MSDTC op server 'server is niet beschikbaar'

  2. 3 manieren om de dagnaam van een datum te krijgen in SQL Server (T-SQL)

  3. Operator

  4. Hoe u deze 3 veelvoorkomende toegangsproblemen kunt oplossen