简体   繁体   English

如何获取 JDBC 中的插入 ID?

[英]How to get the insert ID in JDBC?

I want to INSERT a record in a database (which is Microsoft SQL Server in my case) using JDBC in Java.我想在 Java 中使用 JDBC 在数据库中INSERT一条记录(在我的例子中是 Microsoft SQL 服务器)。 At the same time, I want to obtain the insert ID.同时,我想获取插入ID。 How can I achieve this using JDBC API?如何使用 JDBC API 实现此目的?

If it is an auto generated key, then you can use Statement#getGeneratedKeys() for this.如果它是自动生成的密钥,那么您可以为此使用Statement#getGeneratedKeys() You need to call it on the same Statement as the one being used for the INSERT .您需要在与用于INSERT Statement相同的Statement上调用它。 You first need to create the statement using Statement.RETURN_GENERATED_KEYS to notify the JDBC driver to return the keys.您首先需要使用Statement.RETURN_GENERATED_KEYS创建Statement.RETURN_GENERATED_KEYS以通知 JDBC 驱动程序返回密钥。

Here's a basic example:这是一个基本示例:

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.");
            }
        }
    }
}

Note that you're dependent on the JDBC driver as to whether it works.请注意,您依赖于 JDBC 驱动程序是否工作。 Currently, most of the last versions will work, but if I am correct, Oracle JDBC driver is still somewhat troublesome with this.目前,大多数最新版本都可以工作,但如果我是对的,Oracle JDBC 驱动程序在这方面仍然有些麻烦。 MySQL and DB2 already supported it for ages. MySQL 和 DB2 已经支持它很久了。 PostgreSQL started to support it not long ago. PostgreSQL 不久前开始支持它。 I can't comment about MSSQL as I've never used it.我无法评论 MSSQL,因为我从未使用过它。

For Oracle, you can invoke a CallableStatement with a RETURNING clause or a SELECT CURRVAL(sequencename) (or whatever DB-specific syntax to do so) directly after the INSERT in the same transaction to obtain the last generated key.对于 Oracle,您可以在同一事务中的INSERT之后直接CallableStatement带有RETURNING子句或SELECT CURRVAL(sequencename) (或任何特定于数据库的语法SELECT CURRVAL(sequencename)CallableStatement以获取最后生成的键。 See also this answer .另请参阅此答案

  1. Create Generated Column创建生成的列

    String generatedColumns[] = { "ID" };
  2. Pass this geneated Column to your statement将此生成的 Column 传递给您的语句

    PreparedStatement stmtInsert = conn.prepareStatement(insertSQL, generatedColumns);
  3. Use ResultSet object to fetch the GeneratedKeys on Statement使用ResultSet对象在 Statement 上获取 GeneratedKeys

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

When encountering an 'Unsupported feature' error while using Statement.RETURN_GENERATED_KEYS , try this:在使用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();
}

Where BATCHID is the auto generated id.其中BATCHID是自动生成的 ID。

I'm hitting Microsoft SQL Server 2008 R2 from a single-threaded JDBC-based application and pulling back the last ID without using the RETURN_GENERATED_KEYS property or any PreparedStatement.我正在从基于单线程 JDBC 的应用程序访问 Microsoft SQL Server 2008 R2,并在不使用 RETURN_GENERATED_KEYS 属性或任何 PreparedStatement 的情况下拉回最后一个 ID。 Looks something like this:看起来像这样:

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;
} 

This blog post nicely isolates three main SQL Server "last ID" options: http://msjawahar.wordpress.com/2008/01/25/how-to-find-the-last-identity-value-inserted-in-the-sql-server/ - haven't needed the other two yet.这篇博文很好地隔离了三个主要的 SQL Server“最后一个 ID”选项: http : //msjawahar.wordpress.com/2008/01/25/how-to-find-the-last-identity-value-inserted-in-the -sql-server/ - 还不需要另外两个。

Instead of a comment , I just want to answer post.而不是评论,我只想回答帖子。


Interface java.sql.PreparedStatement接口java.sql.PreparedStatement

  1. columnIndexes « You can use prepareStatement function that accepts columnIndexes and SQL statement. columnIndexes « 您可以使用接受 columnIndexes 和 SQL 语句的 prepareStatement 函数。 Where columnIndexes allowed constant flags are Statement.RETURN_GENERATED_KEYS 1 or Statement.NO_GENERATED_KEYS[2], SQL statement that may contain one or more '?'其中 columnIndexes 允许的常量标志是 Statement.RETURN_GENERATED_KEYS 1或 Statement.NO_GENERATED_KEYS[2],SQL 语句可能包含一个或多个 '?' IN parameter placeholders. IN 参数占位符。

    SYNTAX «句法 ”

     Connection.prepareStatement(String sql, int autoGeneratedKeys) Connection.prepareStatement(String sql, int[] columnIndexes)

    Example:例子:

     PreparedStatement pstmt = conn.prepareStatement( insertSQL, Statement.RETURN_GENERATED_KEYS );

  1. columnNames « List out the columnNames like 'id', 'uniqueID', ... . columnNames «列出 columnNames,如'id', 'uniqueID', ... in the target table that contain the auto-generated keys that should be returned.在包含应返回的自动生成的键的目标表中。 The driver will ignore them if the SQL statement is not an INSERT statement.如果 SQL 语句不是INSERT语句,驱动程序将忽略它们。

    SYNTAX «句法 ”

     Connection.prepareStatement(String sql, String[] columnNames)

    Example:例子:

     String columnNames[] = new String[] { "id" }; PreparedStatement pstmt = conn.prepareStatement( insertSQL, columnNames );

Full Example:完整示例:

public static void insertAutoIncrement_SQL(String UserName, String Language, String Message) {
    String DB_URL = "jdbc:mysql://localhost:3306/test", DB_User = "root", DB_Password = "";

    String insertSQL = "INSERT INTO `unicodeinfo`( `UserName`, `Language`, `Message`) VALUES (?,?,?)";
            //"INSERT INTO `unicodeinfo`(`id`, `UserName`, `Language`, `Message`) VALUES (?,?,?,?)";
    int primkey = 0 ;
    try {
        Class.forName("com.mysql.jdbc.Driver").newInstance();
        Connection conn = DriverManager.getConnection(DB_URL, DB_User, DB_Password);

        String columnNames[] = new String[] { "id" };

        PreparedStatement pstmt = conn.prepareStatement( insertSQL, columnNames );
        pstmt.setString(1, UserName );
        pstmt.setString(2, Language );
        pstmt.setString(3, Message );

        if (pstmt.executeUpdate() > 0) {
            // Retrieves any auto-generated keys created as a result of executing this Statement object
            java.sql.ResultSet generatedKeys = pstmt.getGeneratedKeys();
            if ( generatedKeys.next() ) {
                primkey = generatedKeys.getInt(1);
            }
        }
        System.out.println("Record updated with id = "+primkey);
    } catch (InstantiationException | IllegalAccessException | ClassNotFoundException | SQLException e) {
        e.printStackTrace();
    }
}

I'm using SQLServer 2008, but I have a development limitation: I cannot use a new driver for it, I have to use "com.microsoft.jdbc.sqlserver.SQLServerDriver" (I cannot use "com.microsoft.sqlserver.jdbc.SQLServerDriver").我正在使用SQLServer 2008,但我有一个开发限制:我不能使用新的驱动程序,我必须使用“com.microsoft.jdbc.sqlserver.SQLServerDriver”(我不能使用“com.microsoft.sqlserver.jdbc .SQLServerDriver”)。

That's why the solution conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS) threw a java.lang.AbstractMethodError for me.这就是解决方案conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)为我抛出java.lang.AbstractMethodError的原因。 In this situation, a possible solution I found is the old one suggested by Microsoft: How To Retrieve @@IDENTITY Value Using JDBC在这种情况下,我发现一个可能的解决方案是 Microsoft 建议的旧解决方案: How To Retrieve @@IDENTITY Value Using JDBC

import java.sql.*; 
import java.io.*; 

public class IdentitySample
{
    public static void main(String args[])
    {
        try
        {
            String URL = "jdbc:microsoft:sqlserver://yourServer:1433;databasename=pubs";
            String userName = "yourUser";
            String password = "yourPassword";

            System.out.println( "Trying to connect to: " + URL); 

            //Register JDBC Driver
            Class.forName("com.microsoft.jdbc.sqlserver.SQLServerDriver").newInstance();

            //Connect to SQL Server
            Connection con = null;
            con = DriverManager.getConnection(URL,userName,password);
            System.out.println("Successfully connected to server"); 

            //Create statement and Execute using either a stored procecure or batch statement
            CallableStatement callstmt = null;

            callstmt = con.prepareCall("INSERT INTO myIdentTable (col2) VALUES (?);SELECT @@IDENTITY");
            callstmt.setString(1, "testInputBatch");
            System.out.println("Batch statement successfully executed"); 
            callstmt.execute();

            int iUpdCount = callstmt.getUpdateCount();
            boolean bMoreResults = true;
            ResultSet rs = null;
            int myIdentVal = -1; //to store the @@IDENTITY

            //While there are still more results or update counts
            //available, continue processing resultsets
            while (bMoreResults || iUpdCount!=-1)
            {           
                //NOTE: in order for output parameters to be available,
                //all resultsets must be processed

                rs = callstmt.getResultSet();                   

                //if rs is not null, we know we can get the results from the SELECT @@IDENTITY
                if (rs != null)
                {
                    rs.next();
                    myIdentVal = rs.getInt(1);
                }                   

                //Do something with the results here (not shown)

                //get the next resultset, if there is one
                //this call also implicitly closes the previously obtained ResultSet
                bMoreResults = callstmt.getMoreResults();
                iUpdCount = callstmt.getUpdateCount();
            }

            System.out.println( "@@IDENTITY is: " + myIdentVal);        

            //Close statement and connection 
            callstmt.close();
            con.close();
        }
        catch (Exception ex)
        {
            ex.printStackTrace();
        }

        try
        {
            System.out.println("Press any key to quit...");
            System.in.read();
        }
        catch (Exception e)
        {
        }
    }
}

This solution worked for me!这个解决方案对我有用!

I hope this helps!我希望这有帮助!

You can use following java code to get new inserted id.您可以使用以下 java 代码来获取新插入的 id。

ps = con.prepareStatement(query, Statement.RETURN_GENERATED_KEYS);
ps.setInt(1, quizid);
ps.setInt(2, userid);
ps.executeUpdate();

ResultSet rs = ps.getGeneratedKeys();
if (rs.next()) {
    lastInsertId = rs.getInt(1);
}

It is possible to use it with normal Statement 's as well (not just PreparedStatement )也可以将它与普通Statement一起使用(不仅仅是PreparedStatement

Statement statement = conn.createStatement();
int updateCount = statement.executeUpdate("insert into x...)", Statement.RETURN_GENERATED_KEYS);
try (ResultSet generatedKeys = statement.getGeneratedKeys()) {
  if (generatedKeys.next()) {
    return generatedKeys.getLong(1);
  }
  else {
    throw new SQLException("Creating failed, no ID obtained.");
  }
}

With Hibernate's NativeQuery, you need to return a ResultList instead of a SingleResult, because Hibernate modifies a native query使用Hibernate的NativeQuery,需要返回ResultList而不是SingleResult,因为Hibernate修改了原生查询

INSERT INTO bla (a,b) VALUES (2,3) RETURNING id

like喜欢

INSERT INTO bla (a,b) VALUES (2,3) RETURNING id LIMIT 1

if you try to get a single result, which causes most databases (at least PostgreSQL) to throw a syntax error.如果您尝试获得单个结果,这会导致大多数数据库(至少是 PostgreSQL)抛出语法错误。 Afterwards, you may fetch the resulting id from the list (which usually contains exactly one item).之后,您可以从列表中获取结果 id(通常只包含一个项目)。

In my case ->就我而言->

ConnectionClass objConnectionClass=new ConnectionClass();
con=objConnectionClass.getDataBaseConnection();
pstmtGetAdd=con.prepareStatement(SQL_INSERT_ADDRESS_QUERY,Statement.RETURN_GENERATED_KEYS);
pstmtGetAdd.setString(1, objRegisterVO.getAddress());
pstmtGetAdd.setInt(2, Integer.parseInt(objRegisterVO.getCityId()));
int addId=pstmtGetAdd.executeUpdate();              
if(addId>0)
{
    ResultSet rsVal=pstmtGetAdd.getGeneratedKeys();
    rsVal.next();
    addId=rsVal.getInt(1);
}

If you are using Spring JDBC, you can use Spring's GeneratedKeyHolder class to get the inserted ID.如果您使用的是 Spring JDBC,则可以使用 Spring 的 GeneratedKeyHolder 类来获取插入的 ID。

See this answer... How to get inserted id using Spring Jdbctemplate.update(String sql, obj...args)请参阅此答案... 如何使用 Spring Jdbctemplate.update(String sql, obj...args) 获取插入的 id

If you are using JDBC (tested with MySQL) and you just want the last inserted ID, there is an easy way to get it.如果您使用的是 JDBC(用 MySQL 测试)并且您只想要最后插入的 ID,那么有一个简单的方法来获取它。 The method I'm using is the following:我正在使用的方法如下:

public static Integer insert(ConnectionImpl connection, String insertQuery){

    Integer lastInsertId = -1;
    try{
        final PreparedStatement ps = connection.prepareStatement(insertQuery);
        ps.executeUpdate(insertQuery);
        final com.mysql.jdbc.PreparedStatement psFinal = (com.mysql.jdbc.PreparedStatement) ps;
        lastInsertId = (int) psFinal.getLastInsertID();
        connection.close();
    } catch(SQLException ex){
        System.err.println("Error: "+ex);
    }

    return lastInsertId;
}

Also, (and just in case) the method to get the ConnectionImpl is the following:此外,(以防万一)获取ConnectionImpl的方法如下:

public static ConnectionImpl getConnectionImpl(){
    ConnectionImpl conexion = null;

    final String dbName = "database_name";
    final String dbPort = "3306";
    final String dbIPAddress = "127.0.0.1";
    final String connectionPath = "jdbc:mysql://"+dbIPAddress+":"+dbPort+"/"+dbName+"?autoReconnect=true&useSSL=false";
    
    final String dbUser = "database_user";
    final String dbPassword = "database_password";
    try{
        conexion = (ConnectionImpl) DriverManager.getConnection(connectionPath, dbUser, dbPassword);
    }catch(SQLException e){
        System.err.println(e);
    }
    
    return conexion;
}

Remember to add the connector/J to the project referenced libraries.请记住将连接器/J添加到项目引用的库中。

In my case, the connector/J version is the 5.1.42.就我而言,连接器/J 版本是 5.1.42。 Maybe you will have to apply some changes to the connectionPath if you want to use a more modern version of the connector/J such as with the version 8.0.28.如果您想使用更现代的连接器/J 版本(例如版本 8.0.28),您可能必须对connectionPath应用一些更改。

In the file, remember to import the following resources:在文件中,记得导入以下资源:

import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import com.mysql.jdbc.ConnectionImpl;

Hope this will be helpful.希望这会有所帮助。

Most others have suggested to use JDBC API for this, but personally, I find it quite painful to do with most drivers.大多数其他人都建议为此使用 JDBC API ,但就个人而言,我发现大多数驱动程序都非常痛苦。 When in fact, you can just use a native T-SQL feature, the OUTPUT clause :事实上,您可以只使用本机 T-SQL 功能, OUTPUT子句

try (
    Statement s = c.createStatement();
    ResultSet rs = s.executeQuery(
        """
        INSERT INTO t (a, b)
        OUTPUT id
        VALUES (1, 2)
        """
    );
) {
    while (rs.next())
        System.out.println("ID = " + rs.getLong(1));
}

This is the simplest solution for SQL Server as well as a few other SQL dialects (eg Firebird, MariaDB, PostgreSQL, where you'd use RETURNING instead of OUTPUT ). This is the simplest solution for SQL Server as well as a few other SQL dialects (eg Firebird, MariaDB, PostgreSQL, where you'd use RETURNING instead of OUTPUT ).

I've blogged about this topic more in detail here . 我在这里更详细地讨论了这个主题

Connection cn = DriverManager.getConnection("Host","user","pass");
Statement st = cn.createStatement("Ur Requet Sql");
int ret  = st.execute();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM