Sqlserver
 sql >> Base de Dados >  >> RDS >> Sqlserver

Como obter *tudo* de volta de um procedimento armazenado usando JDBC


Quando executamos um procedimento armazenado em JDBC, recebemos de volta uma série de zero ou mais "resultados". Podemos então processar esses "resultados" sequencialmente chamando CallableStatement#getMoreResults() . Cada "resultado" pode conter
  • zero ou mais linhas de dados que podemos recuperar com um ResultSet objeto,
  • uma contagem de atualização para uma instrução DML (INSERT, UPDATE, DELETE) que podemos recuperar com CallableStatement#getUpdateCount() , ou
  • um erro que gera uma SQLServerException.

Para "Problema 1", o problema geralmente é que o procedimento armazenado não começa com SET NOCOUNT ON; e executa uma instrução DML antes de fazer um SELECT para produzir um conjunto de resultados. A contagem de atualização para o DML é retornada como o primeiro "resultado" e as linhas de dados ficam "presas atrás dele" até chamarmos getMoreResults .

"Edição 2" é essencialmente o mesmo problema. O procedimento armazenado produz um "resultado" (geralmente um SELECT ou possivelmente uma contagem de atualização) antes que o erro ocorra. O erro é retornado em um "resultado" subsequente e não causa uma exceção até que o "recuperemos" usando getMoreResults .

Em muitos casos, o problema pode ser evitado simplesmente adicionando SET NOCOUNT ON; como a primeira instrução executável no procedimento armazenado. No entanto, uma alteração no procedimento armazenado nem sempre é possível e o fato é que para obter tudo de volta do procedimento armazenado, precisamos continuar chamando getMoreResults até que, como diz o Javadoc:
There are no more results when the following is true: 

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

Isso parece bastante simples, mas como de costume, "o diabo está nos detalhes", conforme ilustrado pelo exemplo a seguir. Para um procedimento armazenado do 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

... o seguinte código Java retornará tudo ...
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]");
}

... produzindo a seguinte saída de console:
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]