我正在使用JDBC和HSQLDB 2.2.9。将新行插入数据库并随后保留其id(PK设置为自动增量)值的最有效,最准确的方法是什么?我需要这样做的原因可能很明显,但是我将通过一个示例进行说明:

假设存在一个Customer表,该表具有PersonId字段,该字段具有FK约束,指向Person表中的一行。我想创建一个新的Customer,但是要做到这一点,我需要先创建一个新的Person并使用新的Person.id值设置Customer.PersonId

我已经看到了四种解决方法:

  • 插入Person行,将id字段设置为null。 HSQLDB自动生成下一个id值。然后对Person表执行查询以获取刚刚创建的id值,并使用它创建新的Customer行。

    仅检索单个整数值似乎很昂贵。
  • 获取id表中的下一个Person值,并在INSERT语句中使用它来手动设置Person.id值。使用相同的id值设置Customer.PersonId。无需随后从数据库读取。

    如果获得了id值,则可能会出现不一致,但是另一个连接在执行我的INSERT语句之前在表中执行了INSERT INTO Person...
  • 与上面的选项1一样,执行INSERT语句,将id=null设置为允许自动生成。然后使用getGeneratedKeys方法检索在最后一条语句中生成的键。

    我认为这听起来像是一个不错的选择,但我无法使其正常工作。这是我的代码片段:
    // PreparedStatement prepared previously...
    preparedStatement.executeUpdate();
    ResultSet genKeys = preparedStatement.getGeneratedKeys();
    int id;
    if (genKeys.next()) {
        id = genKeys.getInt(1);
    }
    // Finish up method...
    

    这段代码返回的空ResultSetgenKeys。我使用getGeneratedKeys方法不正确吗?如果我可以解决这个问题,那么这可能是可行的方法。
  • 再次执行INSERT语句,以允许自动生成id。然后立即执行CALL IDENTITY()来检索由连接生成的最后一个id值(如here所述,并在this SO问题中提到)。

    即使我必须执行其他executeQuery,这似乎也是一个合理的选择。从积极的方面来说,我实际上能够使它与以下代码一起使用:
    // INSERT statement executed here...
    statement = connection.createStatement();
    ResultSet rs = statement.executeQuery("CALL IDENTITY();");
    int id;
    if (rs.next()) id = rs.getInt(1);
    // Finish up method...
    

  • 因此,总而言之,我并不为之疯狂的前两个选项。后两个似乎还可以,但是我只能让选项4起作用。首选哪个选项,为什么?如果选项3最好,那我在做什么错呢?另外,还有没有我没有提到的更好的方法?我知道诸如“更好”之类的词可能是主观的,但是我正在使用一个简单的数据库,并且需要最直接的解决方案,该解决方案不会将数据库开放到可能的不一致之处,并且不会增加事务失败率(由于尝试创建带有已存在的id的记录)。

    这似乎是一个基本问题(也是必不可少的),但是我找不到有关最佳方法的太多指导。谢谢。

    编辑:
    我刚刚找到了讨论我的选项3的this问题。根据接受的答案,看来我没有添加启用该功能所需的Statement.RETURN_GENERATED_KEYS参数。我没有在代码片段中显示prepareStatement方法,但我使用的是单参数版本。我需要使用重载的两参数版本重试。

    还有一些其他SO问题与该问题密切相关,与我的问题密切相关。因此,我想我的回答可以被认为是重复的(不确定我之前是如何错过其他问题的)。但是,我仍然希望获得有关一种解决方案是否被认为比其他解决方案更好的指导。现在,如果我让选项3起作用,我可能会同意。

    最佳答案

    我没有足够的声誉来评论neizan的答案,但是这是我解决相同问题的方法:

  • 该列看起来像一个ID列,但是没有定义为IDENTITY。
  • 如上所述,您需要指定RETURN_GENERATED_KEYS。
  • 看起来,如果您依次执行2个INSERT,第二个将不会返回生成的 key 。请改用“CALL IDENTITY()”。

  • 使用HSQLDB 2.2.9的示例:
    CREATE TABLE MY_TABLE (
     ID INTEGER IDENTITY,
     NAME VARCHAR(30)
    )
    

    然后在Java中:
    PreparedStatement result = cnx.prepareStatement(
        "INSERT INTO MY_TABLE(ID, NAME) VALUES(NULL, 'TOM');",
        RETURN_GENERATED_KEYS);
    int updated = result.executeUpdate();
    if (updated == 1) {
        ResultSet generatedKeys = result.getGeneratedKeys();
        if (generatedKeys.next()) {
            int key = generatedKeys.getInt(1);
        }
    }
    

    10-05 19:02
    查看更多