簡體   English   中英

如何獲取 JDBC 中的插入 ID?

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

我想在 Java 中使用 JDBC 在數據庫中INSERT一條記錄(在我的例子中是 Microsoft SQL 服務器)。 同時,我想獲取插入ID。 如何使用 JDBC API 實現此目的?

如果它是自動生成的密鑰,那么您可以為此使用Statement#getGeneratedKeys() 您需要在與用於INSERT Statement相同的Statement上調用它。 您首先需要使用Statement.RETURN_GENERATED_KEYS創建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.");
            }
        }
    }
}

請注意,您依賴於 JDBC 驅動程序是否工作。 目前,大多數最新版本都可以工作,但如果我是對的,Oracle JDBC 驅動程序在這方面仍然有些麻煩。 MySQL 和 DB2 已經支持它很久了。 PostgreSQL 不久前開始支持它。 我無法評論 MSSQL,因為我從未使用過它。

對於 Oracle,您可以在同一事務中的INSERT之后直接CallableStatement帶有RETURNING子句或SELECT CURRVAL(sequencename) (或任何特定於數據庫的語法SELECT CURRVAL(sequencename)CallableStatement以獲取最后生成的鍵。 另請參閱此答案

  1. 創建生成的列

    String generatedColumns[] = { "ID" };
  2. 將此生成的 Column 傳遞給您的語句

    PreparedStatement stmtInsert = conn.prepareStatement(insertSQL, generatedColumns);
  3. 使用ResultSet對象在 Statement 上獲取 GeneratedKeys

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

在使用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();
}

其中BATCHID是自動生成的 ID。

我正在從基於單線程 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;
} 

這篇博文很好地隔離了三個主要的 SQL Server“最后一個 ID”選項: http : //msjawahar.wordpress.com/2008/01/25/how-to-find-the-last-identity-value-inserted-in-the -sql-server/ - 還不需要另外兩個。

而不是評論,我只想回答帖子。


接口java.sql.PreparedStatement

  1. columnIndexes « 您可以使用接受 columnIndexes 和 SQL 語句的 prepareStatement 函數。 其中 columnIndexes 允許的常量標志是 Statement.RETURN_GENERATED_KEYS 1或 Statement.NO_GENERATED_KEYS[2],SQL 語句可能包含一個或多個 '?' IN 參數占位符。

    句法 ”

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

    例子:

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

  1. columnNames «列出 columnNames,如'id', 'uniqueID', ... 在包含應返回的自動生成的鍵的目標表中。 如果 SQL 語句不是INSERT語句,驅動程序將忽略它們。

    句法 ”

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

    例子:

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

完整示例:

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

我正在使用SQLServer 2008,但我有一個開發限制:我不能使用新的驅動程序,我必須使用“com.microsoft.jdbc.sqlserver.SQLServerDriver”(我不能使用“com.microsoft.sqlserver.jdbc .SQLServerDriver”)。

這就是解決方案conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)為我拋出java.lang.AbstractMethodError的原因。 在這種情況下,我發現一個可能的解決方案是 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)
        {
        }
    }
}

這個解決方案對我有用!

我希望這有幫助!

您可以使用以下 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);
}

也可以將它與普通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.");
  }
}

使用Hibernate的NativeQuery,需要返回ResultList而不是SingleResult,因為Hibernate修改了原生查詢

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

喜歡

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

如果您嘗試獲得單個結果,這會導致大多數數據庫(至少是 PostgreSQL)拋出語法錯誤。 之后,您可以從列表中獲取結果 id(通常只包含一個項目)。

就我而言->

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

如果您使用的是 Spring JDBC,則可以使用 Spring 的 GeneratedKeyHolder 類來獲取插入的 ID。

請參閱此答案... 如何使用 Spring Jdbctemplate.update(String sql, obj...args) 獲取插入的 id

如果您使用的是 JDBC(用 MySQL 測試)並且您只想要最后插入的 ID,那么有一個簡單的方法來獲取它。 我正在使用的方法如下:

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

此外,(以防萬一)獲取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;
}

請記住將連接器/J添加到項目引用的庫中。

就我而言,連接器/J 版本是 5.1.42。 如果您想使用更現代的連接器/J 版本(例如版本 8.0.28),您可能必須對connectionPath應用一些更改。

在文件中,記得導入以下資源:

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

希望這會有所幫助。

大多數其他人都建議為此使用 JDBC API ,但就個人而言,我發現大多數驅動程序都非常痛苦。 事實上,您可以只使用本機 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 ).

我在這里更詳細地討論了這個主題

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