【问题标题】:How to get a value from the last inserted row? [duplicate]如何从最后插入的行中获取值? [复制]
【发布时间】:2010-09-19 10:52:03
【问题描述】:

有没有办法从最后插入的行中获取值?

我正在插入一个 PK 会自动增加的行,我想得到这个 PK。只有 PK 保证在表中是唯一的。

我正在将 Java 与 JDBC 和 PostgreSQL 一起使用。

【问题讨论】:

  • 我使用的是 JDBC 4,所以 Statement.RETURN_GENERATED_KEYS 不起作用。我收到此错误消息:“org.postgresql.util.PSQLException:不支持返回自动生成的密钥。”但是 PostgresSQL - RETURNING 确实有效。
  • @Mark Rotteveel 你是如何在这个问题中添加重复项的?这是在 2008 年提出的,声称的重复问题是在 2009 年提出的。它不合适吗?
  • @Haramoz 我关闭的是这个问题的规范副本。年龄不是决定重复目标是什么的重要因素,答案的质量才是。
  • @Mark Rotteveel 好的,明白了!

标签: java database postgresql jdbc


【解决方案1】:

使用 PostgreSQL,您可以通过 RETURNING 关键字来实现:

PostgresSQL - RETURNING

INSERT INTO mytable( field_1, field_2,... )
VALUES ( value_1, value_2 ) RETURNING anyfield

它将返回“anyfield”的值。 “anyfield”可能是一个序列,也可能不是。

要将它与 JDBC 一起使用,请执行以下操作:

ResultSet rs = statement.executeQuery("INSERT ... RETURNING ID");
rs.next();
rs.getInt(1);

【讨论】:

  • 使用返回的“ID”,假设您的 PK 名称是 ID。
  • 这当然是正确的SQL方法,但问题是用JDBC如何做到这一点,而另一半问题是如何执行语句。如其他答案中所述,您要么需要使用executeQuery()(返回一个ResultSet)而不是executeUpdate()(返回更新的行数),要么使用RETURN_GENERATED_KEYS选项来激活调用getGeneratedKeys()之后的方法的能力打电话给executeUpdate()
【解决方案2】:

请参阅java.sql.Statement 的 API 文档。

基本上,当您调用executeUpdate()executeQuery() 时,使用Statement.RETURN_GENERATED_KEYS 常量。然后,您可以调用 getGeneratedKeys 来获取由该执行创建的所有行的自动生成键。 (假设您的 JDBC 驱动程序提供了它。)

大致是这样的:

Statement stmt = conn.createStatement();
stmt.execute(sql, Statement.RETURN_GENERATED_KEYS);
ResultSet keyset = stmt.getGeneratedKeys();

【讨论】:

  • 哦,如果就这么简单。不幸的是,除非您拥有正确的 Postgres 驱动程序版本,否则这不起作用,即使这样,ResultSet 也有 all 字段,而不仅仅是生成的 id 字段。
  • 是的,不适用于 PostgreSQL
  • 据我所知 getGeneratedKeys() 仅适用于实现它的 JDBC 驱动程序。正确的做法是恕我直言,运行 dmd = conn.getMetadata() 然后运行 ​​dmd.supportsGetGeneratedKeys() 以查看是否支持此功能。
  • getGeneratedKeys() 中返回的唯一列是 ROWID - oracle 11。
【解决方案3】:

如果您使用的是 JDBC 3.0,那么您可以在插入 PK 后立即获取它的值。

这里有一篇文章讨论了如何:https://www.ibm.com/developerworks/java/library/j-jdbcnew/

Statement stmt = conn.createStatement();
// Obtain the generated key that results from the query.
stmt.executeUpdate("INSERT INTO authors " +
                   "(first_name, last_name) " +
                   "VALUES ('George', 'Orwell')",
                   Statement.RETURN_GENERATED_KEYS);
ResultSet rs = stmt.getGeneratedKeys();
if ( rs.next() ) {
    // Retrieve the auto generated key(s).
    int key = rs.getInt(1);
}

【讨论】:

【解决方案4】:

自从 PostgreSQL JDBC 驱动程序版本8.4-701 PreparedStatement#getGeneratedKeys() 终于可以完全正常工作了。我们在生产中使用了将近一年,对此我们非常满意。

在“plain JDBC”中,PreparedStatement 需要按如下方式创建以使其返回密钥:

statement = connection.prepareStatement(SQL, Statement.RETURN_GENERATED_KEYS);

您可以下载当前的JDBC驱动版本here(目前还是8.4-701)。

【讨论】:

  • 我使用PreparedStatement.RETURN_GENERATED_KEYSStatement.RETURN_GENERATED_KEYS 有关系吗?
  • @csharpfolk:不。它在StatementPreparedStatement extends Statement 中定义。另请参阅其源代码和 javadoc。
【解决方案5】:

postgresql 中的序列是事务安全的。所以你可以使用

currval(sequence)

Quote:

曲线

返回由 nextval 为这个序列最近获得的值 在当前会话中。 (错误是 如果 nextval 从未被报告过 在这个中要求这个序列 会话。)请注意,因为这是 返回一个会话本地值,它 给出一个可预测的答案,即使 其他会话正在执行 nextval 同时。

【讨论】:

  • 有没有可能另一个事务可以改变他的 INSERT 和他的 SELECT currval() 之间的序列值?我假设这些操作中的每一个都将在单独的事务中进行。
  • 没有。这正是 currval 函数的用途。
【解决方案6】:

根据此处的答案,这是我解决它的方法:

Connection conn = ConnectToDB(); //ConnectToDB establishes a connection to the database.
String sql = "INSERT INTO \"TableName\"" +
        "(\"Column1\", \"Column2\",\"Column3\",\"Column4\")" +
        "VALUES ('value1',value2, 'value3', 'value4') RETURNING 
         \"TableName\".\"TableId\"";
PreparedStatement prpState = conn.prepareStatement(sql);
ResultSet rs = prpState.executeQuery();
if(rs.next()){
      System.out.println(rs.getInt(1));
        }

【讨论】:

    【解决方案7】:

    如果您使用的是Statement,请执行以下操作

    //MY_NUMBER is the column name in the database 
    String generatedColumns[] = {"MY_NUMBER"};
    Statement stmt = conn.createStatement();
    
    //String sql holds the insert query
    stmt.executeUpdate(sql, generatedColumns);
    
    ResultSet rs = stmt.getGeneratedKeys();
    
    // The generated id
    
    if(rs.next())
    long key = rs.getLong(1);
    

    如果您使用的是PreparedStatement,请执行以下操作

    String generatedColumns[] = {"MY_NUMBER"};
    PreparedStatement pstmt = conn.prepareStatement(sql,generatedColumns);
    pstmt.setString(1, "qwerty");
    
    pstmt.execute();
    ResultSet rs = pstmt.getGeneratedKeys();
    if(rs.next())
    long key = rs.getLong(1);
    

    【讨论】:

      【解决方案8】:

      在 postgres 中为 id 列使用序列:

      INSERT mytable(myid) VALUES (nextval('MySequence'));
      
      SELECT currval('MySequence');
      

      currval 将返回同一会话中序列的当前值。

      (在 MS SQL 中,您将使用 @@identity 或 SCOPE_IDENTITY())

      【讨论】:

        【解决方案9】:
        PreparedStatement stmt = getConnection(PROJECTDB + 2)
            .prepareStatement("INSERT INTO fonts (font_size) VALUES(?) RETURNING fonts.*");
        stmt.setString(1, "986");
        ResultSet res = stmt.executeQuery();
        while (res.next()) {
            System.out.println("Generated key: " + res.getLong(1));
            System.out.println("Generated key: " + res.getInt(2));
            System.out.println("Generated key: " + res.getInt(3));
        }
        stmt.close();
        

        【讨论】:

          【解决方案10】:

          不要使用 SELECT currval('MySequence') - 该值会在插入失败时递增。

          【讨论】:

          • 那又怎样?他只是要求唯一值,而不是连续值(这在具有并行可中止事务的系统中是不可能实现的目标)。
          • 原始问题:“有没有办法从最后插入的行中获取值?”使用 currval(seq) 形成 SELECT 语句以获取最后插入的行的代码可能无法产生预期的结果。
          【解决方案11】:

          对于带有注释和 Postgresql 驱动程序 9.0-801.jdbc4 的 MyBatis 3.0.4,您可以在 Mapper 中定义一个接口方法,例如

          public interface ObjectiveMapper {
          
          @Select("insert into objectives" +
                  " (code,title,description) values" +
                  " (#{code}, #{title}, #{description}) returning id")
          int insert(Objective anObjective);
          

          请注意,使用@Select 而不是@Insert。

          【讨论】:

            【解决方案12】:

            例如:

            连接 conn = null; PreparedStatement sth = null; 结果集 rs =null; 尝试 { conn = delegate.getConnection(); sth = conn.prepareStatement(INSERT_SQL); sth.setString(1, pais.getNombre()); sth.executeUpdate(); rs=sth.getGeneratedKeys(); 如果(rs.next()){ 整数 id = (整数) rs.getInt(1); pais.setId(id); } }

            ,Statement.RETURN_GENERATED_KEYS);" 未找到。

            【讨论】:

              【解决方案13】:

              使用那个简单的代码:

              // Do your insert code
              
              myDataBase.execSQL("INSERT INTO TABLE_NAME (FIELD_NAME1,FIELD_NAME2,...)VALUES (VALUE1,VALUE2,...)");
              
              // Use the sqlite function "last_insert_rowid"
              
              Cursor last_id_inserted = yourBD.rawQuery("SELECT last_insert_rowid()", null);
              
              // Retrieve data from cursor.
              
              last_id_inserted.moveToFirst(); // Don't forget that!
              
              ultimo_id = last_id_inserted.getLong(0);  // For Java, the result is returned on Long type  (64)
              

              【讨论】:

                【解决方案14】:

                如果您在事务中,您可以在插入后使用SELECT lastval() 来获取最后生成的 id。

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 2013-05-15
                  • 1970-01-01
                  • 1970-01-01
                  • 2023-03-29
                  • 1970-01-01
                  • 2019-11-21
                  • 2014-09-10
                  • 2014-07-07
                  相关资源
                  最近更新 更多