【问题标题】:getGeneratedKeys() returns an Empty ResultSetgetGeneratedKeys() 返回一个空结果集
【发布时间】:2021-02-20 00:17:38
【问题描述】:

您好,感谢您的阅读。 我正在尝试检索新插入数据的 ID,但我总是得到一个空的 ResultSet。

 Connection con = main.getCon();
 String sqlCommand = "Insert Into Relations(name,explanation) values(?,?)";
 PreparedStatement state = 
 con.prepareStatement(sqlCommand,Statement.RETURN_GENERATED_KEYS);
 state.setString(1,name.getText());
 state.setString(2,explanation.getText());
 int affectedRows = state.executeUpdate();
 assert (affectedRows>0);
        
 ResultSet rs = state.getGeneratedKeys();
 assert rs.next();
 int instertedID= rs.getInt("ID");

不知道有什么问题。在线检查了不同的样本,但无法弄清楚我的错误是什么。 我也用 Statement 尝试过,但也没有运气。
第1点:代码运行顺利,我的数据插入数据库。 第2点:网上有这个案例的例子,你可以在这里查看: https://www.baeldung.com/jdbc-returning-generated-keys

我刚刚意识到我的 ResultSet 不是空的,我在使用调试器时遇到了问题,这就是为什么我认为它是空的。 正如 Mark Rotteveel 在评论中提到的,问题出在“断言”声明上。

【问题讨论】:

  • 您使用的是哪个数据库系统?您的问题很可能是您正在使用assert rs.next()。在正常的 Java 执行中(除非指定了 -ea),断言被禁用,因此不会执行,这意味着您永远不会将结果集放在第一行。请改用if (rs.next()) { ... }
  • 您是对的,先生,断言是问题所在。您能否将其作为答案,以便我可以选择它并关闭该主题?非常感谢。很抱歉,我的调试器显示我的 ResultSet 为空,这就是我为我的问题选择这个标题的原因。我用过德比。
  • 我已根据我的评论添加了答案。

标签: java jdbc derby


【解决方案1】:

问题在于您使用了assert rs.next()。 Java 中的断言用于检查不变量(例如在测试期间),但是当您正常运行 Java 时,assert 语句不会执行,它们仅在使用-ea 命令行选项显式启用时才会执行。

因此,rs.next() 不会被调用,因此当您调用rs.getInt(1) 时,您的结果集仍然位于第一行之前。而是使用if (rs.next()) { ... }

【讨论】:

    【解决方案2】:

    这取决于数据库引擎。一些提示:

    JDBC 是低级的,不适合编程

    这是一个复杂的 API。使用更容易的东西:JDBI 或 JOOQ。他们可能对插入有抽象,可以为您处理这些内容。

    某些数据库引擎要求您列出列名

    试试:

    con.prepareStatement(sqlCommand, new String[] {"UNID"});

    某些数据库引擎只会将生成的值作为直接结果集返回

    不要打电话给.executeUpdate();相反,调用.executeQuery(),它返回一个ResultSet;检查那个。

    别的东西

    如果上述方法没有帮助,请发布您正在使用的确切表结构和数据库引擎。

    你的代码坏了

    你不能创建资源对象(一旦必须关闭),除非你这样做是安全的,而且你这样做并不安全。使用 try-with-resources:

    String sql = "INSERT INTO relations(name, explanation) VALUES (?, ?)";
    try (Connection con = main.getCon();
        PreparedStatement ps = con.prepareStatement(sql, new String[] {"unid"})) {
    
        state.setString(1, name.getText());
        state.setString(2, explanation.getText());
        try (ResultSet rs = state.executeQuery()) {
            if (!rs.next()) throw new SQLException("insert didn't return autogen?");
            System.out.println(rs.getInt(1));
        }
    }
    

    ResultSets、Statements、PreparedStatements 和 Connections 都是资源(必须关闭!)——如果你想在一个字段中存储这些东西之一,你可以这样做,但前提是包含该字段的类是它本身一个资源:它必须有一个close()方法,它必须实现AutoClosable,然后你只能像上面那样使用try-with-resources来创建这个类的实例。

    未能遵守这些规则意味着您的应用似乎可以运行,但在运行时会泄漏资源,因此,如果您让它运行足够长的时间,它就会开始崩溃。此外,随着越来越多的连接处于打开状态并永远卡住,您的数据库引擎将停止运行。

    【讨论】:

    • 是的,你是对的,但我没有复制/粘贴所有代码。此代码在 Try 块内运行。
    【解决方案3】:

    将最后一行代码更改为此,因为您使用的 DBMS 可能不支持按列名获取值,因此请传递该列的索引:

    int instertedID = rs.getInt(1); 
    

    【讨论】:

    • 问题是,ResultSet 本身是空的。
    【解决方案4】:
    String sqlCommand = "Insert Into Relations (name, explanation) values(?, ?)";
    PreparedStatement state = con.prepareStatement(sqlCommand, Statement.RETURN_GENERATED_KEYS);
     state.setString(1,name.getText());
     state.setString(2,explanation.getText());
     state.executeUpdate();
     
    
    ResultSet resultSet = state.getGeneratedKeys();
    
    if(resultSet.next()) {
        System.out.println(resultSet.getInt(1)); //Indicate the corresponding column index value.
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多