Jak uzyskać insert ID w JDBC?

Chcę INSERT zapis w bazie danych (którą w moim przypadku jest Microsoft SQL Server) przy użyciu JDBC w Javie. Jednocześnie chcę uzyskać identyfikator insert. Jak mogę to osiągnąć za pomocą JDBC API?

Author: Aurgho Bhattacharjee, 2009-12-16

10 answers

Jeśli jest to automatycznie wygenerowany klucz, możesz użyć Statement#getGeneratedKeys() za to. Musisz zadzwonić do tego samego Statement, co ten, który jest używany do INSERT. Najpierw musisz utworzyć instrukcję używając Statement.RETURN_GENERATED_KEYS aby powiadomić sterownik JDBC o zwróceniu kluczy.

Oto podstawowy przykład:

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

Zauważ, że jesteś zależny od sterownika JDBC, czy działa. Obecnie większość ostatnich wersji będzie działać, ale jeśli się nie mylę, Oracle JDBC driver jest nadal trochę kłopotliwe z tym. MySQL i DB2 wspierały go już od wieków. PostgreSQL zaczął go wspierać nie tak dawno temu. Nie mogę skomentować MSSQL, ponieważ nigdy go nie używałem.

W przypadku Oracle można wywołać CallableStatement z klauzulą RETURNING lub SELECT CURRVAL(sequencename) (lub inną składnią specyficzną dla DB) bezpośrednio po INSERT w tej samej transakcji, aby uzyskać ostatni wygenerowany klucz. Patrz również ta odpowiedź .
 564
Author: BalusC,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-02 18:59:55
  1. Utwórz Wygenerowaną Kolumnę

    String generatedColumns[] = { "ID" };
    
  2. Przekaż tę kolumnę do swojego oświadczenia

    PreparedStatement stmtInsert = conn.prepareStatement(insertSQL, generatedColumns);
    
  3. Użyj obiektu ResultSet do pobrania GeneratedKeys na instrukcji

    ResultSet rs = stmtInsert.getGeneratedKeys();
    
    if (rs.next()) {
        long id = rs.getLong(1);
        System.out.println("Inserted ID -" + id); // display inserted record
    }
    
 9
Author: Harsh Maheswari,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-16 16:10:08

Uderzam w Microsoft SQL Server 2008 R2 z jednowątkowej aplikacji opartej na JDBC i pobieram ostatni identyfikator bez użycia właściwości RETURN_GENERATED_KEYS lub jakiegokolwiek PreparedStatement. Wygląda mniej więcej tak:

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

Ten wpis na blogu ładnie izoluje trzy główne opcje SQL Server "last ID": http://msjawahar.wordpress.com/2008/01/25/how-to-find-the-last-identity-value-inserted-in-the-sql-server/{[5]- nie potrzebowałem jeszcze dwóch pozostałych.

 8
Author: ftexperts,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-03 08:41:23

Zgodnie z błędem 'nieobsługiwana funkcja' przy użyciu Statement.RETURN_GENERATED_KEYS, spróbuj tak:

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

    }

Gdzie BRANCHID jest automatycznie wygenerowanym identyfikatorem

 5
Author: Eitan Rimon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-12-14 08:39:14

Używam SQLServer 2008, ale mam ograniczenie rozwoju: nie mogę użyć do tego nowego sterownika, muszę użyć "com.microsoft.jdbc.sqlserver.SQLServerDriver "(nie mogę użyć " com.microsoft.sqlserver.jdbc.SQLServerDriver").

Dlatego rozwiązanie conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS) rzuciło Javę.lang.AbstractMethodError dla mnie. W tej sytuacji możliwym rozwiązaniem, które znalazłem, jest stare zaproponowane przez Microsoft: Jak odzyskać wartość @ @ IDENTITY używając 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)
        {
        }
    }
}

To rozwiązanie zadziałało dla mnie!

Mam nadzieję, że to pomoże!
 3
Author: xanblax,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-09-10 09:41:04

Zamiast komentować chcę tylko odpowiedzieć na post.


Interfejs java.sql.PreparedStatement

  1. ColumnIndexes " Możesz użyć funkcji preparstatement, która akceptuje columnIndexes i polecenie SQL. gdzie columnIndexes dozwolone stałe flagi są instrukcjami.RETURN_GENERATED_KEYS1 lub Statement. NO_GENERATED_KEYS[2], polecenie SQL, które może zawierać jeden lub więcej '?"W parametrze zastępcze.

    składnia "

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

    Przykład:

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

  1. Nazwy kolumn " lista nazw kolumn, takich jak 'id', 'uniqueID', .... w tabeli docelowej, która zawiera automatycznie wygenerowane klucze, które należy zwrócić. Driver je zignoruje, jeśli instrukcja SQL nie jest instrukcją INSERT.

    Składnia "

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

    Przykład:

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

Pełny Przykład:

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();
    }
}
 0
Author: Yash,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-18 13:20:04

Z natywnym zapytaniem Hibernate, musisz zwrócić listę wyników zamiast Pojedynczejsult, ponieważ Hibernate modyfikuje natywne zapytanie

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

Jak

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

Jeśli próbujesz uzyskać pojedynczy wynik, który powoduje, że większość baz danych (przynajmniej PostgreSQL) wyświetla błąd składni. Następnie możesz pobrać wynikowy identyfikator z listy (który zwykle zawiera dokładnie jeden element).

 0
Author: Balin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-23 08:26:35

Można go używać również z normalnymi Statement (nie tylko 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
Author: rogerdpack,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-04 19:33:08

W moim przypadku - >

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);
}
 0
Author: TheSagya,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-29 13:37:58
Connection cn = DriverManager.getConnection("Host","user","pass");
Statement st = cn.createStatement("Ur Requet Sql");
int ret  = st.execute();
 -5
Author: Abdelkhalek Benhoumine,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-14 23:00:57