有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java如何使用JDBC从存储过程中获取*everything*

在使用JDBC处理SQL Server存储过程时,我偶尔会遇到两种形式的奇怪行为:

问题1:我在SQLServerManagementStudio(SSMS)中运行了一个存储过程,它返回一个结果集。然而,当我尝试

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

我有个例外

com.microsoft.sqlserver.jdbc.SQLServerException: The statement did not return a result set.

问题2:我在SSMS中运行了一个存储过程,它会引发一个错误,但是当我使用JDBC来.execute存储过程时,不会引发异常

为什么会出现这些问题?我如何避免这些问题


共 (1) 个答案

  1. # 1 楼答案

    当我们在JDBC中执行一个存储过程时,我们会返回一系列零或更多的“结果”。然后我们可以通过调用CallableStatement#getMoreResults()顺序处理这些“结果”。每个“结果”可以包含

    • 我们可以使用ResultSet对象检索的零行或多行数据
    • DML语句(INSERT、update、DELETE)的更新计数,我们可以使用CallableStatement#getUpdateCount()检索,或者
    • 引发SQLServerException的错误

    对于“问题1”,问题通常是存储过程不是以SET NOCOUNT ON;开头,而是在执行SELECT以生成结果集之前执行DML语句。DML的更新计数作为第一个“结果”返回,数据行“卡在后面”,直到我们调用getMoreResults

    “问题2”本质上是同一个问题。在错误发生之前,存储过程会生成一个“结果”(通常是SELECT,也可能是update count)。该错误会在后续的“结果”中返回,并且不会导致异常,直到我们使用getMoreResults来“检索”它

    在许多情况下,只需在存储过程中添加SET NOCOUNT ON;作为第一个可执行语句,就可以避免这个问题。然而,对存储过程的更改并不总是可能的,事实仍然是,为了从存储过程中获取所有信息,我们需要继续调用^{,直到,正如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 'dbo.@tbl'. 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]