2017-02-10 51 views
3

我偶爾會遇到兩種形式的怪異行爲使用JDBC與SQL Server存儲過程來工作時,存儲過程回:如何獲得*一切*使用JDBC

問題1:我運行一個存儲過程SQL Server Management Studio(SSMS)並返回一個結果集。然而,當我嘗試

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

我得到的異常

com.microsoft.sqlserver.jdbc.SQLServerException:聲明沒有返回結果集。

問題2:我運行SSMS一個存儲過程,它提出了一個錯誤,但是當我使用JDBC來.execute存儲過程不會拋出異常。

爲什麼會出現這些問題,我該如何避免它們?

+0

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

回答

2

當我們在JDBC中執行一個存儲過程時,我們得到一系列零或多個「結果」。然後我們可以通過調用CallableStatement#getMoreResults()來順序處理這些「結果」。每一個「結果」可能包含

  • 行,我們可以用一個ResultSet對象中檢索數據,
  • 的DML語句(INSERT,UPDATE,DELETE),我們可以用CallableStatement#getUpdateCount(),或
  • 檢索更新計數
  • 引發SQLServerException的錯誤。

對於「問題1」,問題通常是存儲過程不是以SET NOCOUNT ON;開頭,並在執行SELECT之前執行DML語句以生成結果集。 DML的更新計數作爲第一個「結果」返回,並且數據行「卡在它後面」,直到我們調用getMoreResults

「問題2」基本上是同樣的問題。在發生錯誤之前,存儲過程會生成一個「結果」(通常是一個SELECT,或者可能是一個更新計數)。該錯誤將在後續的「結果」中返回,並且在使用getMoreResults「檢索」它之前不會導致異常。

在許多情況下,只需將SET NOCOUNT ON;作爲存儲過程中的第一個可執行語句添加即可避免此問題。但是,更改了存儲過程並不總是可能的,但事實是,爲了得到一切從存儲過程前,我們需要不斷的打電話getMoreResults最後,到了Javadoc中說:

There are no more results when the following is true: 

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

那聽起來很簡單,但像往常一樣,「魔鬼在細節中」,如下面的例子所示。對於SQL Server存儲過程...

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 

...下面的Java代碼將返回一切......

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

...產生以下控制檯輸出:

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]