如何在JDBC中获取插入ID?

Sat*_*tya 366 java sql insert-id jdbc

我希望INSERT在Java中使用JDBC中的数据库(在我的情况下是Microsoft SQL Server)中的记录.同时,我想获取插入ID.如何使用JDBC API实现此目的?

Bal*_*usC 622

如果它是自动生成的密钥,那么您可以使用Statement#getGeneratedKeys()它.你需要调用它与Statement用于的那个相同INSERT.首先需要创建语句,Statement.RETURN_GENERATED_KEYS以通知JDBC驱动程序返回密钥.

这是一个基本的例子:

public void create(User user) throws SQLException {
    try (
        Connection connection = dataSource.getConnection();
        PreparedStatement statement = connection.prepareStatement(SQL_INSERT,
                                      Statement.RETURN_GENERATED_KEYS);
    ) {
        statement.setString(1, user.getName());
        statement.setString(2, user.getPassword());
        statement.setString(3, user.getEmail());
        // ...

        int affectedRows = statement.executeUpdate();

        if (affectedRows == 0) {
            throw new SQLException("Creating user failed, no rows affected.");
        }

        try (ResultSet generatedKeys = statement.getGeneratedKeys()) {
            if (generatedKeys.next()) {
                user.setId(generatedKeys.getLong(1));
            }
            else {
                throw new SQLException("Creating user failed, no ID obtained.");
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,您是否依赖JDBC驱动程序来确定它是否有效.目前,大多数最新版本都可以使用,但如果我是正确的,Oracle JDBC驱动程序仍然有点麻烦.MySQL和DB2已经支持它多年了.不久前PostgreSQL开始支持它.我无法评论MSSQL,因为我从未使用它.

对于Oracle,您可以在同一事务中直接调用CallableStatement带有RETURNING子句或SELECT CURRVAL(sequencename)(或任何特定于DB的语法)INSERT来获取最后生成的密钥.另见这个答案.

  • NOT设置Statement.RETURN_GENERATED_KEYS选项的一个有趣的副作用是错误消息,这是完全模糊的"必须在获得任何结果之前执行该语句". (7认同)
  • 如果DB返回生成的密钥,`generatedKeys.next()`返回`true`.看,这是一个`ResultSet`.`close()`只是为了释放资源.否则,您的数据库将长期耗尽它们,您的应用程序将会中断.你只需要自己编写一些实用程序方法来执行结束任务.另见[this](http://stackoverflow.com/questions/3148092/java-jdbc-mysql-connector-how-to-resolve-disconnection-after-a-long-idle-time/3148857#3148857)和[这个](http://stackoverflow.com/questions/2313197/jdbc-mysql-connection-pooling-practices/2313262#2313262)回答. (7认同)
  • 在插入之前获取序列中的下一个值比在插入之后获取currval更好,因为后者可能在多线程环境(例如,任何Web应用程序容器)中返回错误的值.JTDS MSSQL驱动程序支持getGeneratedKeys. (4认同)
  • (应该澄清我通常使用Oracle,因此对JDBC驱动程序的功能的期望非常低). (4认同)
  • 大多数数据库/驱动程序的正确答案.但对于Oracle来说,这不起作用.对于Oracle,请更改为:connection.prepareStatement(sql,new String [] {"PK column name"}); (4认同)
  • @JeeBee:据我所知,如果您使用*相同*语句(在同一事务内),则这不适用。IIRC Hibernate 也是这样工作的。 (2认同)

小智 20

  1. 创建生成的列

    String generatedColumns[] = { "ID" };
    
    Run Code Online (Sandbox Code Playgroud)
  2. 将这个生成的列传递给你的陈述

    PreparedStatement stmtInsert = conn.prepareStatement(insertSQL, generatedColumns);
    
    Run Code Online (Sandbox Code Playgroud)
  3. 使用ResultSetobject获取Statement上的GeneratedKeys

    ResultSet rs = stmtInsert.getGeneratedKeys();
    
    if (rs.next()) {
        long id = rs.getLong(1);
        System.out.println("Inserted ID -" + id); // display inserted record
    }
    
    Run Code Online (Sandbox Code Playgroud)


fte*_*rts 8

我从单线程基于JDBC的应用程序中攻击Microsoft SQL Server 2008 R2并在不使用RETURN_GENERATED_KEYS属性或任何PreparedStatement的情况下撤回最后一个ID.看起来像这样:

private int insertQueryReturnInt(String SQLQy) {
    ResultSet generatedKeys = null;
    int generatedKey = -1;

    try {
        Statement statement = conn.createStatement();
        statement.execute(SQLQy);
    } catch (Exception e) {
        errorDescription = "Failed to insert SQL query: " + SQLQy + "( " + e.toString() + ")";
        return -1;
    }

    try {
        generatedKey = Integer.parseInt(readOneValue("SELECT @@IDENTITY"));
    } catch (Exception e) {
        errorDescription = "Failed to get ID of just-inserted SQL query: " + SQLQy + "( " + e.toString() + ")";
        return -1;
    }

    return generatedKey;
} 
Run Code Online (Sandbox Code Playgroud)

这篇博文很好地隔离了三个主要的SQL Server"最后ID"选项:http: //msjawahar.wordpress.com/2008/01/25/how-to-find-the-last-identity-value-inserted-in-the -sql-server / - 还不需要其他两个.

  • 应用程序只有一个线程不会使竞争条件无法进行:如果两个客户端插入一行并使用您的方法检索ID,则可能会失败. (4认同)

Yas*_*ash 7

我不想发表评论,只想回复帖子。

\n\n
\n\n

接口java.sql.PreparedStatement

\n\n
    \n
  1. columnIndexes \xc2\xab 您可以使用接受columnIndexes 和SQL 语句的prepareStatement 函数。\n其中columnIndexes 允许的常量标志为Statement.RETURN_GENERATED_KEYS 1或Statement.NO_GENERATED_KEYS[2],SQL 语句可能包含一个或多个\'?\' IN 参数占位符。

    \n\n

    语法 \xc2\xab \n

    \n\n
    Connection.prepareStatement(String sql, int autoGeneratedKeys)\nConnection.prepareStatement(String sql, int[] columnIndexes)\n
    Run Code Online (Sandbox Code Playgroud)\n\n\n\n

    例子:

    \n\n\n\n
    PreparedStatement pstmt = \n    conn.prepareStatement( insertSQL, Statement.RETURN_GENERATED_KEYS );\n
    Run Code Online (Sandbox Code Playgroud)\n\n
  2. \n
\n\n
\n\n
    \n
  1. columnNames \xc2\xab列出列名称,例如\'id\', \'uniqueID\', .... 在包含应返回的自动生成键的目标表中。如果 SQL 语句不是INSERT语句,驱动程序将忽略它们。

    \n\n

    语法 \xc2\xab

    \n\n\n\n
    Connection.prepareStatement(String sql, String[] columnNames)\n
    Run Code Online (Sandbox Code Playgroud)\n\n\n\n

    例子:

    \n\n\n\n
    String columnNames[] = new String[] { "id" };\nPreparedStatement pstmt = conn.prepareStatement( insertSQL, columnNames );\n
    Run Code Online (Sandbox Code Playgroud)\n\n
  2. \n
\n\n
\n\n

完整示例:

\n\n
public static void insertAutoIncrement_SQL(String UserName, String Language, String Message) {\n    String DB_URL = "jdbc:mysql://localhost:3306/test", DB_User = "root", DB_Password = "";\n\n    String insertSQL = "INSERT INTO `unicodeinfo`( `UserName`, `Language`, `Message`) VALUES (?,?,?)";\n            //"INSERT INTO `unicodeinfo`(`id`, `UserName`, `Language`, `Message`) VALUES (?,?,?,?)";\n    int primkey = 0 ;\n    try {\n        Class.forName("com.mysql.jdbc.Driver").newInstance();\n        Connection conn = DriverManager.getConnection(DB_URL, DB_User, DB_Password);\n\n        String columnNames[] = new String[] { "id" };\n\n        PreparedStatement pstmt = conn.prepareStatement( insertSQL, columnNames );\n        pstmt.setString(1, UserName );\n        pstmt.setString(2, Language );\n        pstmt.setString(3, Message );\n\n        if (pstmt.executeUpdate() > 0) {\n            // Retrieves any auto-generated keys created as a result of executing this Statement object\n            java.sql.ResultSet generatedKeys = pstmt.getGeneratedKeys();\n            if ( generatedKeys.next() ) {\n                primkey = generatedKeys.getInt(1);\n            }\n        }\n        System.out.println("Record updated with id = "+primkey);\n    } catch (InstantiationException | IllegalAccessException | ClassNotFoundException | SQLException e) {\n        e.printStackTrace();\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n


Eit*_*mon 5

根据'不支持的功能'使用时出错Statement.RETURN_GENERATED_KEYS,试试这个:

    String[] returnId = { "BATCHID" };
    String sql = "INSERT INTO BATCH (BATCHNAME) VALUES ('aaaaaaa')";
    PreparedStatement statement = connection
            .prepareStatement(sql, returnId);
    int affectedRows = statement.executeUpdate();

    if (affectedRows == 0) {
        throw new SQLException("Creating user failed, no rows affected.");
    }

    try (ResultSet rs = statement.getGeneratedKeys()) {
        if (rs.next()) {
            System.out.println(rs.getInt(1));
        }
        rs.close();

    }
Run Code Online (Sandbox Code Playgroud)

BRANCHID是自动生成的ID