Làm thế nào để có được ID chèn trong JDBC?


385

Tôi muốn INSERTmột bản ghi trong cơ sở dữ liệu (đó là Microsoft SQL Server trong trường hợp của tôi) bằng cách sử dụng JDBC trong Java. Đồng thời, tôi muốn lấy ID chèn. Làm cách nào tôi có thể đạt được điều này bằng cách sử dụng API JDBC?


Để lại id được AutoGenrerated trong Truy vấn String sql = "INSERT INTO 'yash'.'mytable' ('name') VALUES (?)"; int primkey = 0 ; PreparedStatement pstmt = con.prepareStatement(sql, new String[] { "id" }/*Statement.RETURN_GENERATED_KEYS*/); pstmt.setString(1, name); if (pstmt.executeUpdate() > 0) { java.sql.ResultSet generatedKeys = pstmt.getGeneratedKeys (); if (generatedKeys.next()) primkey = generatedKeys.getInt(1); }
Yash

Câu trả lời:


650

Nếu đó là một khóa được tạo tự động, thì bạn có thể sử dụng Statement#getGeneratedKeys()cho việc này. Bạn cần gọi nó giống Statementnhư cái đang được sử dụng cho INSERT. Trước tiên bạn cần tạo câu lệnh bằng cách sử dụng Statement.RETURN_GENERATED_KEYSđể thông báo cho trình điều khiển JDBC để trả về các khóa.

Đây là một ví dụ cơ bản:

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

Lưu ý rằng bạn phụ thuộc vào trình điều khiển JDBC xem nó có hoạt động không. Hiện tại, hầu hết các phiên bản cuối cùng sẽ hoạt động, nhưng nếu tôi chính xác, trình điều khiển JDBC của Oracle vẫn hơi rắc rối với điều này. MySQL và DB2 đã hỗ trợ nó từ lâu. PostgreSQL bắt đầu hỗ trợ nó cách đây không lâu. Tôi không thể nhận xét về MSSQL vì tôi chưa bao giờ sử dụng nó.

Đối với Oracle, bạn có thể gọi một CallableStatementvới một RETURNINGkhoản hoặc một SELECT CURRVAL(sequencename)(hoặc bất kỳ DB cụ thể cú pháp để làm như vậy) ngay sau INSERTtrong cùng một giao dịch để có được chìa khóa tạo ra cuối cùng. Xem thêm câu trả lời này .


4
Tốt hơn là lấy giá trị tiếp theo trong chuỗi trước khi chèn hơn là lấy giá trị dòng sau khi chèn, bởi vì giá trị sau có thể trả về giá trị sai trong môi trường đa luồng (ví dụ: bất kỳ vùng chứa ứng dụng web nào). Trình điều khiển MSSQL của JTDS hỗ trợ getGeneratedKeys.
JeeBee

4
(nên làm rõ rằng tôi thường sử dụng Oracle, do đó, có những kỳ vọng rất thấp về khả năng của trình điều khiển JDBC thông thường).
JeeBee

7
Một tác dụng phụ thú vị của việc KHÔNG đặt tùy chọn Statement.RETURN_GENERATED_KEYS là thông báo lỗi, đây là thông báo hoàn toàn tối nghĩa "Câu lệnh phải được thực thi trước khi có bất kỳ kết quả nào."
Chris Winters

7
Trả generatedKeys.next()về truenếu DB trả về một khóa được tạo. Hãy nhìn xem, đó là một ResultSet. Đây close()chỉ là tài nguyên miễn phí. Nếu không, DB của bạn sẽ hết chúng trong thời gian dài và ứng dụng của bạn sẽ bị hỏng. Bạn chỉ cần tự viết ra một số phương thức tiện ích để thực hiện nhiệm vụ đóng. Xem thêm nàynày câu trả lời.
BalusC

5
Câu trả lời đúng cho hầu hết các cơ sở dữ liệu / trình điều khiển. Đối với Oracle, điều này không hoạt động. Đối với Oracle, thay đổi thành: Connection.prepareStatement (sql, String mới [] {"tên cột PK"});
Darrell Teague

24
  1. Tạo cột được tạo

    String generatedColumns[] = { "ID" };
  2. Vượt qua cột bị biến đổi này để tuyên bố của bạn

    PreparedStatement stmtInsert = conn.prepareStatement(insertSQL, generatedColumns);
  3. Sử dụng ResultSetđối tượng để tìm nạp GeneratedKeys trên Statement

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

8

Tôi đang nhấn Microsoft SQL Server 2008 R2 từ một ứng dụng dựa trên JDBC đơn luồng và lấy lại ID cuối cùng mà không sử dụng thuộc tính RETURN_GENERATED_KEYS hoặc bất kỳ PreparedStatement nào. Trông giống như thế này:

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

Bài đăng trên blog này cách ly độc đáo ba tùy chọn "ID cuối cùng" của SQL Server: http://msjawahar.wordpress.com/2008/01/11/how-to-find-the-last-identity-value-inserted-in-the -sql-server / - chưa cần hai cái kia.


4
Ứng dụng chỉ có một luồng không làm cho điều kiện cuộc đua là không thể: nếu hai máy khách chèn một hàng và lấy ID bằng phương thức của bạn, nó có thể thất bại.
11684

Tại sao bạn? Tôi chỉ vui mừng vì tôi không phải là người nghèo khổ phải gỡ lỗi mã của bạn khi cho phép nhiều luồng!
mjaggard

6

Khi gặp lỗi 'Tính năng không được hỗ trợ' trong khi sử dụng Statement.RETURN_GENERATED_KEYS, hãy thử điều này:

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

Trong trường hợp BATCHIDlà tự động tạo id.


ý bạn làBATCHID
MoolsBytheway

Câu trả lời chính xác!!!
Hasitha Jayawardana

3

Tôi đang sử dụng SQLServer 2008, nhưng tôi có một hạn chế phát triển: Tôi không thể sử dụng trình điều khiển mới cho nó, tôi phải sử dụng "com.microsoft.jdbc.sqlserver.SQLServerDriver" (Tôi không thể sử dụng "com.microsoft.sqlserver.jdbc .QueryServerDriver ").

Đó là lý do tại sao giải pháp conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)đã ném java.lang.Ab tríchMethodError cho tôi. Trong tình huống này, một giải pháp khả thi mà tôi tìm thấy là giải pháp cũ được đề xuất bởi Microsoft: Cách truy xuất @@ IDENTITY Giá trị bằng cách sử dụng 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)
        {
        }
    }
}

Giải pháp này đã làm việc cho tôi!

Tôi hi vọng cái này giúp được!


1

Thay vì một bình luận , tôi chỉ muốn trả lời bài viết.


Giao diện java.sql.PreparedStatement

  1. cột Indexes «Bạn có thể sử dụng hàm readyStatement chấp nhận cột Index và câu lệnh SQL. Trong đó các cột chỉ số cho phép các cờ không đổi được cho phép là Statement.RETURN_GENERATED_KEYS 1 hoặc Statement.NO_GENERATED_KEYS [2], câu lệnh SQL có thể chứa một hoặc nhiều '?' IN giữ chỗ tham số.

    ĐỒNG HỒ

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

    Thí dụ:

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

  1. cộtNames « Liệt kê các cộtNames như thế nào 'id', 'uniqueID', .... trong bảng đích chứa các khóa được tạo tự động sẽ được trả về. Trình điều khiển sẽ bỏ qua chúng nếu câu lệnh SQL không phải là một INSERTcâu lệnh.

    ĐỒNG HỒ

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

    Thí dụ:

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

Ví dụ đầy đủ:

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

Có an toàn khi sử dụng giải pháp này trong môi trường thời gian chạy đa luồng không?
Nguyên mẫu

1

Bạn có thể sử dụng mã java sau để có id mới được chèn.

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

0

Với NativeQuery của Hibernate, bạn cần trả về Danh sách kết quả thay vì SingleResult, vì Hibernate sửa đổi truy vấn gốc

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

giống

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

nếu bạn cố gắng để có được một kết quả duy nhất, điều này khiến hầu hết các cơ sở dữ liệu (ít nhất là PostgreSQL) gặp lỗi cú pháp. Sau đó, bạn có thể tìm nạp id kết quả từ danh sách (thường chứa chính xác một mục).


0

Cũng có thể sử dụng nó với bình thường Statement(không chỉ 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.");
  }
}

0

Trong trường hợp của tôi ->

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

Tuy nhiên, tôi nghĩ rằng đó là cách tiếp cận dài để có được nó. Tôi nghĩ rằng sẽ có nhiều giải pháp nén cũng.
TheSagya


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

Xin lỗi, nhưng cái này được cho là gì?
MoolsBytheway

1. createStatementPhương pháp từ Connectionkhông mong đợi bất kỳ params. 2. executePhương thức từ Statementmong đợi một Chuỗi với Truy vấn. 3. executePhương thức trả về: truenếu kết quả đầu tiên là một ResultSetđối tượng; falsenếu nó là một số cập nhật hoặc không có kết quả. docs.oracle.com/javase/7/docs/api/java/sql/ Kẻ
atilacamurca
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.