7

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

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

我已经看到了四种方法来解决这个问题:

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

    仅检索单个整数值似乎很昂贵。

  2. 获取表中的下一个id值并在语句中Person使用它来手动设置值。使用相同的值进行设置。不需要从 DB 进行后续读取。INSERTPerson.ididCustomer.PersonId

    id如果获得了一个值,可能会出现不一致,但是在执行INSERT我的INSERT INTO Person...语句之前,另一个连接在表中执行了一个。

  3. 执行INSERT语句,如上面的选项 1,设置id=null为允许自动生成。然后使用该getGeneratedKeys方法检索上一条语句中生成的键。

    我认为这听起来是一个不错的选择,但我无法让它发挥作用。这是我的代码片段:

    // PreparedStatement prepared previously...
    preparedStatement.executeUpdate();
    ResultSet genKeys = preparedStatement.getGeneratedKeys();
    int id;
    if (genKeys.next()) {
        id = genKeys.getInt(1);
    }
    // Finish up method...
    

    此代码返回一个空ResultSet的 for genKeys。我是否getGeneratedKeys错误地使用了该方法?如果我能让这个工作,这可能是要走的路。

  4. 再次,执行INSERT允许自动生成的语句id。然后立即执行CALL IDENTITY()以检索id连接生成的最后一个值(如here所述和this SO question中所述)。

    这似乎也是一个合理的选择,即使我必须执行额外的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。根据接受的答案,我似乎遗漏了Statement.RETURN_GENERATED_KEYS启用该功能所需的参数。我没有prepareStatement在我的代码片段中显示该方法,但我使用的是单参数版本。我需要使用重载的双参数版本重试。

还有一些其他的 SO 问题与我的问题密切相关。所以,我想我的可能被认为是重复的(不知道我之前是如何错过其他问题的)。但我仍然想要任何关于一种解决方案是否被认为比其他解决方案更好的指导。现在,如果我让选项 3 起作用,我可能会接受。

4

2 回答 2

8

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

  • 该列看起来像一个 ID 列,但它没有定义为 IDENTITY;
  • 如上所述,您需要指定 RETURN_GENERATED_KEYS。
  • 看起来如果您按顺序执行 2 INSERT,第二个不会返回生成的密钥。请改用“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);
    }
}
于 2013-09-09T09:05:40.797 回答
1

这里没有太多动作,所以我会继续回答以结束这个问题。在尝试了不同的选项之后,在看到这个问题之后,我能够让我的选项 3 起作用。就像我在问题的编辑中提到的那样,我将使用选项 3。选项 4 也可以正常工作,但是由于对链接问题的接受答案是由信誉良好的来源给出的,所以我坚持这一点。我希望在开始这个问题之前我已经看到了这个问题/答案,我会节省一些时间!

于 2013-07-25T16:44:49.873 回答