当前位置: 首页 > 知识库问答 >
问题:

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

饶骁
2023-03-14

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

问题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存储过程时,没有引发异常。

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

共有1个答案

易炳
2023-03-14

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

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

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

“问题2”本质上是同一问题。存储过程在错误发生之前生成“结果”(通常是SELECT,也可能是update计数)。该错误在随后的“结果”中返回,并且在使用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 '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]
 类似资料:
  • 问题内容: 我有一个用户定义的类型,它是SqlServer数据库中的数据表。我一直在使用Database,DbCommand等调用存储过程并获取数据集。数据集很方便,因为它们可以包含多个表。 现在我想传入一个数据表,所以我尝试了: 但是我收到一个异常,指出“ @BASE_ITEMIDS”不是正确的类型:“传入的表格格式数据流(TDS)远程过程调用(RPC)协议流不正确。参数1(\“ @ BASE_

  • 我有一个非常复杂的oracle存储过程,它搜索和检索一些数据。该过程返回一个输出参数-oracle游标。我通过JDBC执行过程: 问题是,查询有时会花费相当长的时间(几分钟),我希望用户能够通过单击按钮随时取消查询。我引用了stmt对象,但不幸的是(从其他线程)调用stmt.cancel()没有效果。 另一方面,当我将CallableStatement sql更改为以下查询时: 在调用stmt.c

  • 主要内容:创建CallableStatement对象,关闭CallableStatement对象,JDBC SQL转义语法在讨论JDBC Statement教程文章时,我们已经学习了如何在JDBC中使用存储过程。 本教程文章与该部分类似,但它将讲解演示有关JDBC SQL转义语法的其他信息。 就像对象创建和对象一样,它可使用同样的方式创建对象,该对象将用于执行对数据库存储过程的调用。 创建CallableStatement对象 假设需要执行以下Oracle存储过程 - 注意:上面的存储过程是为O

  • 我创建了一个存储过程,如下所示: //mysql中的存储过程 并且我正在调用Java中的存储过程,如下所示: //使用JDBC调用存储过程 但它显示了一个编译时异常,如下所示: CAN在参数中为存储的函数调用的返回值设置。

  • 问题内容: 我有一个需要很多参数的插入存储过程-其中2个是@ FirstName,@ LastName。我还有一个更新存储过程,该过程需要许多参数-其中2个是@ FirstName,@ LastName。 我想做的是,从插入SP内部完成之后,调用更新SP并将其发送给@ FirstName,@ LastName。 我不知道这样做的正确语法;我试过了: 但我认为这是错误的。 有人可以告诉我怎么写这个电

  • 问题内容: 我使用的是SQL Server2005。我创建了一个大多数时候都可以使用的存储过程,但是我发现了一个实例,它无法满足我的要求。 目前,代码执行的操作是这样的 输出变量在哪里。 因此,我可以得到一个数字,但我真正想要的只是SQL错误。 像 Hey之类的 东西 ,我不能这样做,因为在此列上有fk约束 或其他 约束 。我在msdn http://msdn.microsoft.com/zh-