【问题标题】:Return sequence number on insert插入时返回序列号
【发布时间】:2019-03-25 03:56:21
【问题描述】:

通过我的 OrderDAO 对象将订单插入 HSQL 数据库后,我希望能够检索在插入时分配给订单的序列号。

我有这个给我的PreparedStatement

public long saveOrder(Order order) {
    long orderId = 0;
    try (Connection conn = MyDataSource.getDataSource().getConnection();
         PreparedStatement ps = conn.prepareStatement("INSERT INTO orders(id, order_number) VALUES (NEXT VALUE FOR seq1, ?)",
                                                            PreparedStatement.RETURN_GENERATED_KEYS)) {

        ps.setString(1, order.getOrderNumber());
        ps.execute();

        ResultSet rs = ps.getResultSet();
        if (rs.next()) {
            orderId = rs.getLong(1);
        }

    } catch (Exception e) {
        throw new RuntimeException(e);
    }
    return orderId;
}

我会假设,在执行查询并询问其结果集之后,结果集中的第一列将是序列号。但我似乎不是那样工作的。

我哪里错了?

【问题讨论】:

  • 向我们展示您尝试过的代码,同时请告诉我们您正在使用哪个数据库和驱动程序。并非所有数据库都支持生成的密钥,并且并非所有数据库都以相同的方式支持它。
  • 添加了代码。我正在使用内存中的 hsqldb。
  • 执行第一个查询以从序列中获取下一个值,然后将结果用作第二个查询的参数。

标签: java sql jdbc hsqldb


【解决方案1】:

你的代码有两个问题:

  1. 您使用错误的方法来检索生成的密钥结果集。生成的键结果集只能使用getGeneratedKeys() 检索(或者至少,这是 JDBC 规范所要求的)。

    您需要更改代码以使用ps.getGeneratedKeys() 而不是ps.getResultSet()

  2. 另一个问题是您的代码假定非标准生成键行为:您的插入实际上并未使用 JDBC 规范所期望的生成键,因为您自己在插入语句中生成标识符(使用 NEXT VALUE FOR seq1 ),而不是作为插入语句的副作用生成键(例如,通过标识列或触发器)。

    在这种情况下,HSQLDB 不会返回生成的键,因为它不会将 id 视为生成的列。相反,您需要将该列定义为标识列(并且不要在插入中明确指定它),或者明确指定要返回的列。

    有关创建标识列,请参阅 HSQLDB 文档。显式指定要返回的列替换

    conn.prepareStatement("<query>", PreparedStatement.RETURN_GENERATED_KEYS)
    

    使用要返回的列的索引规范(即1 是第一列):

    conn.prepareStatement("<query>", new int[] { 1 })
    

    或要返回的列的列名规范

    conn.prepareStatement("<query>", new String[] { "id" })
    

你的最终代码应该是这样的:

try (PreparedStatement ps = conn.prepareStatement(
         "INSERT INTO orders(id, order_number) VALUES (NEXT VALUE FOR seq1, ?)",
         new String[] { "id" })) {
    ps.setString(1, order.getOrderNumber());
    ps.execute();

    try (ResultSet rs = stmt.getGeneratedKeys()) {
        if (rs.next()) {
            return rs.getLong(1);
        }
    }
}

【讨论】:

    【解决方案2】:

    基于this SO question and answer,你的语法应该是这样的:

    Connection conn = MyDataSource.getDataSource().getConnection();
    long orderId = 0;
    String sql = "INSERT INTO orders (id, order_number) VALUES (NEXT VALUE FOR seq1, ?)";
    PreparedStatement ps = conn.prepareStatement(sql, RETURN_GENERATED_KEYS);
    ps.setString(1, order.getOrderNumber());
    int numAffected = ps.executeUpdate();
    ResultSet rs = ps.getGeneratedKeys();
    if (rs.next()) {
        orderId = rs.getLong(1);
    }
    

    【讨论】:

    • 完全不需要检查更新计数
    • @MarkRotteveel entirely unnecessary ...所以您是说不存在 DML 更新计数不是一个的情况?
    • 这是一个插入,要么成功并插入一行,要么失败并出现异常。还要考虑更新计数很棘手,并且在某些数据库中即使只插入了 1 行也可能返回高于 1 的值(例如,如果触发器插入了审计记录,这可能包含在某些数据库系统的更新计数中)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-03-09
    • 1970-01-01
    • 2010-10-20
    • 2015-10-08
    • 1970-01-01
    • 1970-01-01
    • 2011-11-11
    相关资源
    最近更新 更多