2017-02-10 15 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-raced-error-handling-in-multi-statement-stored-procedures –

ответ

2

Когда мы выполняем хранимую процедуру в JDBC, мы возвращаем серию нулевых или более «результатов». Затем мы можем обрабатывать эти «результаты» последовательно, вызывая CallableStatement#getMoreResults(). Каждый «результат» может содержать

  • строк данных, которые мы можем получить с ResultSet объекта,
  • счетчика изменений для заявления DML (INSERT, UPDATE, DELETE), которые мы можем получить с CallableStatement#getUpdateCount() или
  • Ошибка, которая генерирует исключение SQLServerException.

Для «Выпуск 1» проблемы часто, что хранимая процедура не начинается с SET NOCOUNT ON; и выполняет оператор DML перед выполнением SELECT, чтобы произвести набор результатов. Количество обновлений для 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 процедуры сервера хранятся ...

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] 

 Смежные вопросы

  • Нет связанных вопросов^_^