【问题标题】:What does it mean when Statement.executeUpdate() returns -1?Statement.executeUpdate() 返回 -1 是什么意思?
【发布时间】:2012-09-06 05:30:31
【问题描述】:

在管理工作室和executeUpdate 中工作的查询使相同的executeUpdate 返回-1,这在我们可以找到的任何文档中都未定义。它应该只返回行数或0。这是什么意思?如果这很重要,驱动程序就是 JDBC-ODBC 桥。

例子:

String query = "IF NOT EXISTS (SELECT * FROM animals WHERE animal_name ='" + a +"') INSERT INTO " + table + " (animal_name, animal_desc, species_id) VALUES ('" + a + "', '" + b + "', " + c + ")";
int result = statement.executeUpdate(query);
System.out.println(result);

查询有效,当行被添加到数据库时,它返回 -1 很奇怪,文档说它只会返回 0 或行数(我已经更正了)。

更新:

在 Management Studio 中运行此命令会导致“命令成功完成”。

IF NOT EXISTS (SELECT * FROM animals WHERE animal_name = 'a') 
INSERT INTO animals(animal_name, animal_desc, species_id) VALUES ('a', 'a', 1)

这应该意味着该方法应该返回 0,因为它不返回任何内容,对吗?

【问题讨论】:

  • 你能给我们一段代码吗?
  • 我很想看看你的代码。而且你有点不正确,当你说executeUpdate(...) 应该只返回 1 或 0...它实际上返回 要么 (1) SQL 数据操作的行数语言 (DML) 语句或 (2) 0 用于不返回任何内容的 SQL 语句
  • 不能为 -1。 executeUpdate() 为 INSERT、UPDATE 或 DELETE 语句返回行数,或者为不返回任何内容的 SQL 语句返回 0
  • 用代码和更正更新了问题。
  • @MaVRoSCy 是的,我们使用的是 SQL Server 2008。

标签: java sql sql-server-2008 jdbc


【解决方案1】:

4 年后,Microsoft has open sourced their JDBC driver on Github。今天接到了这个问题的通知,去看看,相信我找到了罪魁祸首here,mssql-jdbc/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java:1713

基本上,如果 SQL Server 发回的不是明确的结果集,驱动程序会尝试了解它。根据 cmets,它是这样的:

  1. 首先检查错误。 (1669 年)

  2. 不是错误。是结果集吗? (ln 1680)

  3. 不是错误或结果集。也许是 T-SQL 语句的结果? 即,以下之一:

    • 受影响的行数的正计数(来自 INSERT、UPDATE 或 DELETE),
    • 零表示没有行受到影响,或者语句是 DDL,或者
    • -1 表示语句成功,但没有可用的更新计数信息(转换为批量更新计数数组中的 Statement.SUCCESS_NO_INFO)。 (1706 年)
  4. 以上都不是。最后一次机会...进入上面的解析器,我们知道 moreResults 最初是正确的。如果我们的 moreResults 为 false,则我们点击了一个 DONE 令牌(DONE(FINAL)或 DONE(批处理中的 RPC)),表明批处理总体成功,但没有关于单个语句的更新计数的信息。这类似于上面的最后一种情况,只是没有更新计数。即:我们有一个成功的结果(返回 true),但我们没有关于它的其他信息(updateCount = -1)。 (1693 年)

  5. 到达这里的唯一方法(moreResults 仍然正确,但没有任何明显的结果)是如果 TDSParser 没有实际解析 任何东西。也就是说,我们在响应中处于 EOF。在那种情况下,真的没有更多的结果。我们完成了。 (1717 年)

(强调我的)

所以你们最后是对的。 SQL 根本无法判断有多少行受到影响,默认为-1。 :)

【讨论】:

    【解决方案2】:

    由于执行的语句实际上不是 DML(例如 UPDATEINSERTEXECUTE),而是一段 包含 DML 的 T-SQL,我怀疑它没有被处理作为更新查询。

    JDBC 4.1 规范的第 13.1.2.3 节陈述了一些事情(顺便说一句很难解释):

    execute方法返回true时,调用getResultSet方法 检索 ResultSet 对象。当execute 返回 false 时,该方法 getUpdateCount 返回一个整数。如果这个数大于或等于零,它 表示语句返回的更新计数。如果为-1,则表示存在 没有更多的结果。

    鉴于这些信息,我猜executeUpdate() 内部会执行execute(),然后 - 因为execute() 将返回false - 它会返回getUpdateCount() 的值,在这种情况下 - 根据使用 JDBC 规范 - 将返回 -1

    这一事实进一步证实了这一点 1) Statement.executeUpdate() 的 Javadoc 说:

    返回:(1) SQL 数据操作语言 (DML) 语句的行数或 (2) 0 用于不返回任何内容的 SQL 语句

    以及 2) Statement.getUpdateCount() 的 Javadoc 指定:

    当前结果作为更新计数; -1 如果当前结果是 ResultSet 对象或没有更多结果

    澄清一下:鉴于executeUpdate() 的Javadoc,行为可能是错误的,但可以解释。

    正如我在其他地方评论的那样,-1 可能只是表示:可能发生了一些变化,但我们根本不知道,或者我们无法给出准确的变化数量(例如,因为在这个例子中它是一块执行的 T-SQL)。

    【讨论】:

    • 这可能是对观察到的 SQL Server JDBC 驱动程序行为的最有用的解释。好主意!
    【解决方案3】:

    这并没有解释为什么会这样,但它解释了为什么会发生这种情况。以下字节码将-1 设置为SQLServerStatement 构造函数中的内部updateCount 标志:

    // Method descriptor #401 (Lcom/microsoft/sqlserver/jdbc/SQLServerConnection;II)V
    // Stack: 5, Locals: 8
    SQLServerStatement(
      com.microsoft.sqlserver.jdbc.SQLServerConnection arg0, int arg1, int arg2) 
    throws com.microsoft.sqlserver.jdbc.SQLServerException;
    
    // [...]
    
    34 aload_0 [this]
    35 iconst_m1
    36 putfield com.microsoft.sqlserver.jdbc.SQLServerStatement.updateCount:int [27]
    

    现在,我不会分析所有可能的控制流,但我只想说这是内部默认初始化值,它会以某种方式泄露给客户端代码。注意,这也可以在其他方法中完成:

    // Method descriptor #383 ()V
    // Stack: 2, Locals: 1
    final void resetForReexecute() 
    throws com.microsoft.sqlserver.jdbc.SQLServerException;
    
    // [...]
    
    10 aload_0 [this]
    11 iconst_m1
    12 putfield com.microsoft.sqlserver.jdbc.SQLServerStatement.updateCount:int [27]
    
    // Method descriptor #383 ()V
    // Stack: 3, Locals: 3
    final void clearLastResult();
    0 aload_0 [this]
    1 iconst_m1
    2 putfield com.microsoft.sqlserver.jdbc.SQLServerStatement.updateCount:int [27]
    

    换句话说,您可能-1 解释为与0 相同。如果您依赖此结果值,请保持安全并按以下方式进行检查:

    // No rows affected
    if (stmt.executeUpdate() <= 0) {
    }
    // Rows affected
    else {
    }
    

    更新:在阅读 Mark Rotteveel's answer 时,我倾向于同意他的观点,假设 -1“未知更新计数” 的 JDBC 兼容值。即使相关方法的 Javadoc 中没有记录,它也记录在 JDBC specs, chapter 13.1.2.3 Returning Unknown or Multiple Results 中。在这种情况下,可以说IF .. INSERT .. 语句将具有“未知更新计数”,因为该语句无论如何都不符合 SQL 标准。

    【讨论】:

      【解决方案4】:

      对于针对 DB2 for z/OS 服务器的 executeUpdate 语句,返回的值取决于正在执行的 SQL 语句的类型:

      对于可以具有更新计数的 SQL 语句,例如 INSERT、UPDATE 或 DELETE 语句,返回值是受影响的行数。可以是:

      一个正数,如果操作影响的行数为正数,并且该操作不是对分段表空间的批量删除。

      0,如果没有行受操作影响。

      -1,如果操作是对分段表空间的批量删除。

      对于 DB2 CALL 语句,返回值 -1,因为 DB2 数据库服务器无法确定受影响的行数。对 CALL 语句的 getUpdateCount 或 getMoreResults 的调用也返回 -1。 对于任何其他 SQL 语句,返回值 -1。

      【讨论】:

      • 虽然它是评论的一部分,但我相信 OP 指定使用的数据库是 SQL Server 2008
      • @Sachin TRUNCATE 命令是否算作“分段表上的批量删除”?
      【解决方案5】:

      我也没有在任何地方看到过这种情况,但我的直觉是这意味着IF 阻止了整个语句的执行。

      尝试使用IF 传递的数据库运行语句。

      还要检查是否涉及任何可能改变结果的触发器。

      [编辑]the standard says 认为这个函数永远不应该返回-1,这并没有强制。 Java 没有前置条件和后置条件。 JDBC 驱动程序可以返回一个随机数,并且无法阻止它。

      如果知道为什么会发生这种情况很重要,请针对不同的数据库运行该语句,直到您尝试了所有执行路径(即,IF 返回 false 的路径和返回 true 的路径)。

      如果它不那么重要,请将其标记为 Microsoft 工程师的“聪明把戏”,并记住当你下次想自己聪明时有多喜欢它。

      【讨论】:

      • IF 语句有效,代码几乎是从 Management Studio 中的查询复制粘贴的,该查询在放入程序之前经过测试。 INSERT 语句确实有效,因为如果您之后执行 SELECT 语句,则会显示该行。
      • 我的意思是“执行”,如“它是否更改了数据库”而不是“它是否产生了错误”
      • 查看我对问题的编辑。我们从查询中得到的只是“命令已成功完成”
      • SQL Server JDBC 驱动程序不是唯一违反 JDBC API 合同的驱动程序。见this question about MySQL's Statement.setFetchSize()
      • 所有 JDBC 驱动程序都有……“对标准的有趣解释”。
      猜你喜欢
      • 2021-01-24
      • 1970-01-01
      • 1970-01-01
      • 2014-04-15
      • 2018-06-09
      • 1970-01-01
      • 1970-01-01
      • 2013-04-25
      • 1970-01-01
      相关资源
      最近更新 更多