2017-02-10 1 views
3

ich gelegentlich zwei Formen von seltsamen Verhalten in denen mithilfe von JDBC mit SQL Server gespeicherte Prozeduren arbeiten:Wie * alles bekommen * zurück von einer gespeicherten Prozedur mit JDBC

Problem 1: Ich betreiben eine gespeicherte Prozedur in SQL Server Management Studio (SSMS) und gibt eine Ergebnismenge zurück. Allerdings, wenn ich versuche

try (CallableStatement cs = conn.prepareCall("{call dbo.TroublesomeSP}")) { 
    ResultSet rs = cs.executeQuery(); 

ich die Ausnahme

com.microsoft.sqlserver.jdbc.SQLServerException erhalten: Die Anweisung keine Ergebnismenge zurückkehrte.

Problem 2: Ich betreibe eine gespeicherte Prozedur in SSMS und es löst einen Fehler, aber wenn ich JDBC verwenden, um .execute die gespeicherte Prozedur keine Ausnahme ausgelöst.

Warum treten diese Probleme auf und wie kann ich sie vermeiden?

+0

Verwandte: http://stackoverflow.com/questions/14213824/java-sql-statement-hasresultset und http://stackoverflow.com/questions/24694442/expecting-multiple- resultsets-but-only-get-one und http://stackoverflow.com/questions/32561550/jdbc-sql-server-raised-error-handling-in-multi-statement-stored-procedures –

Antwort

2

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

  • Reihen von Daten, die wir mit einem ResultSet Objekt,
  • eine Aktualisierungszählwert für eine DML-Anweisung abrufen können (INSERT, UPDATE, DELETE), die wir mit CallableStatement#getUpdateCount() abrufen können, oder
  • ein Fehler, der eine SQLServerException auslöst.

Für „Ausgabe 1“ das Problem ist oft, dass die gespeicherte Prozedur beginnt nicht mit SET NOCOUNT ON; und führt eine DML-Anweisung, bevor Sie eine SELECT tun eine Ergebnismenge zu erzeugen. Die Aktualisierungsanzahl für die DML wird als erstes "Ergebnis" zurückgegeben, und die Datenzeilen bleiben "festgefahren", bis wir getMoreResults aufrufen.

"Problem 2" ist im Wesentlichen das gleiche Problem. Die gespeicherte Prozedur erzeugt ein "Ergebnis" (normalerweise eine SELECT- oder möglicherweise eine Aktualisierungsanzahl), 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 durch einfaches Hinzufügen SET NOCOUNT ON; als erste ausführbare Anweisung in der gespeicherten Prozedur vermieden werden. Allerdings ist eine Änderung der gespeicherten Prozedur nicht immer möglich, und die Tatsache bleibt, dass, um alles zurück aus der gespeicherten Prozedur erhalten wir getMoreResults bis anrufen, halten müssen wie die Javadoc sagt:

There are no more results when the following is true: 

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

That klingt einfach genug, aber wie immer "der Teufel steckt im Detail", wie das folgende Beispiel zeigt. Bei einer SQL Server gespeicherte 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 wird alles zurückgeben ...

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

...Herstellung der folgenden 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] 
Verwandte Themen