【问题标题】:JDBC SQL Server Stored Procedure with ResultSet, return value, and output parameters带有 ResultSet、返回值和输出参数的 JDBC SQL Server 存储过程
【发布时间】:2021-06-29 00:20:42
【问题描述】:

我正在将应用程序从 Jython 转换为已编译的 Java。该应用程序使用大量 SQL Server 存储过程来执行 CRUD 操作。所有过程都定义了一个返回值,该值指示状态,以及一些用于向应用程序提供反馈的输出参数。大多数过程还返回一个结果集。我正在为如何检索返回值、结果集和输出参数而苦苦挣扎。

我通常使用 C#,因此 JDBC 的细微差别对我来说是新的。我一直在测试其中一个向数据库插入然后对插入的对象进行选择的过程。

这是一个简化的示例过程仅用于说明目的。实际程序比这更复杂。

CREATE PROCEDURE [dbo].[sp_Thing_Add]
(
   @Name NVARCHAR(50),
   @Description NVARCHAR(100),
   @ResultMessage NVARCHAR(200) = N'' OUTPUT
)
AS
BEGIN
   SET NOCOUNT ON
   
   DECLARE @Result INT = -1
   DECLARE @ResultMessage = 'Procedure incomplete'
   
   BEGIN TRY
      INSERT INTO Things (Name, Description) VALUES (@Name, @Description)

      SELECT * FROM Things WHERE ThingID = SCOPE_IDENTITY()
   END TRY
   BEGIN CATCH
      SELECT @Result = CASE WHEN ERROR_NUMBER() <> 0 THEN ERROR_NUMBER() ELSE 1 END,
         @ResultMessage = ERROR_MESSAGE()
      GOTO EXIT_SUB
   END CATCH
SUCCESS:
   SET @Result = 0
   SET @ResultMessage = N'Procedure completed successfully'
   RETURN @Result
EXIT_SUB:
   IF @Result <> 0
   BEGIN
   -- Do some error handling stuff
   END
   RETURN @Result

      

我可以使用以下代码成功检索 ResultSet。

var conn = myConnectionProvider.getConnection();
String sql = "{? = call dbo.sp_Thing_Add(?, ?, ?)}"

call = conn.prepareCall(sql);
call.registerOutParameter(1, TYPES.Integer); // Return value
call.setString("Name", thing.getName());
call.setString("Description", thing.getDescription());
call.registerOutParameter("ResultMessage", TYPES.NVARCHAR);
ResultSet rs = call.executeQuery();

// Try to get the return value. This appears to close the ResultSet and prevents data retrieval.
//int returnValue = call.getInt(1);
// Normally there'd be a check here to make sure things executed properly, 
// and if necessary the output parameter(s) may also be leveraged

if (rs.next()) {
   thing.setId(rs.getLong("ThingID"));
   // Other stuff actually happens here too...
}

如果我尝试使用已注释掉的行检索返回值,则会收到一条错误消息,指出 ResultSet 已关闭。

com.microsoft.sqlserver.jdbc.SQLServerException:结果集已关闭。

我浏览了文档并了解了如何处理返回值、输出参数和结果集。但是我怎样才能充分利用这三个方面呢?

【问题讨论】:

  • 不确定,但return 值通常很难处理,而且使用另一个output 参数更容易。无论如何,为什么要搞砸捕获错误并返回它们,只是让它们冒泡回到客户端?顺便说一句,除非涉及触发器,否则您可以将主要代码简化为 insert ... output inserted.* values ... 而不是额外的 select
  • 总是需要在调用结果集的getter之前调用next(),但这里的问题是你混淆了输出参数(通常通过@987654328访问@) 与存储过程生成的结果集中的值。
  • @Charlieface 我同意,如果我从头开始,我会按照你的建议重写程序。但是,由于这是在处理数百个已经存在并且已经在生产环境中运行了一段时间的存储过程,我不想重写它们。
  • 鉴于存储过程中的处理顺序,我猜您需要在 使用CallableStatement.getXXX 检索返回值之前处理结果集;但我手头没有 SQL Server 来测试它。
  • @MarkRotteveel 我知道这是很久以前的事了,但我们完全按照您的建议进行操作,并在获取输出参数之前处理了 ResultSet,这一切都不同了。

标签: java sql-server jdbc callable-statement


【解决方案1】:

鉴于存储过程中的处理顺序(插入、选择、然后填充结果参数),您需要在使用CallableStatement.getXXX 检索返回值之前处理结果集。 p>

【讨论】:

    【解决方案2】:

    输出在从executeQuery()检索的ResultSet rs中。

    您可能希望这样使用执行方法:

        call.execute();
        String returnValue = call.getString("ResultMessage");
    

    您还想正确映射到输出类型。

    【讨论】:

    • 我不确定你是否理解这个问题。我知道如何使用 call.execute() 获取输出参数的值,尽管我在上面的示例代码中使用了 call.executeQuery()。我还需要获取返回值和结果集。
    【解决方案3】:

    执行查询后,您的连接将关闭。基本上 mysql jdbc 连接隐式扩展到 AutoCloseable。由于您的结果只是过程中的实体,请通过索引 0 获取值并进行适当的索引超出范围异常处理。

    【讨论】:

    • 这是完全错误的。 AutoCloseable 并不意味着连接会自动关闭,它只是意味着它可以用作 try-with-resources 语句中的资源。 JDBC 没有指定在执行查询后应该关闭连接,并且驱动程序中的这种行为将是一个严重的错误。另外 JDBC 是基于 1 的,而不是基于 0 的,并且 OP 使用的是 Microsoft SQL Server,而不是 MySQL。
    • 感谢@MarkRotteveel。你的回答很有帮助。
    猜你喜欢
    • 2013-01-27
    • 2017-04-05
    • 1970-01-01
    • 1970-01-01
    • 2020-07-02
    • 2010-11-23
    • 2012-12-29
    • 2012-02-02
    • 2018-07-26
    相关资源
    最近更新 更多