Jak uzyskać identyfikator wstawiania w JDBC?

385

chcę INSERT zapisać w bazie danych (w moim przypadku Microsoft SQL Server) przy użyciu JDBC w Javie. Jednocześnie chcę uzyskać identyfikator wkładki. Jak mogę to osiągnąć za pomocą JDBC API?

Satya
źródło
Pozostaw identyfikator, który jest AutoGenrerated w zapytaniu 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

Odpowiedzi:

650

Jeśli jest to klucz generowany automatycznie, możesz Statement#getGeneratedKeys()do tego użyć . Musisz wywołać go tak samo, Statementjak ten używany w INSERT. Najpierw musisz utworzyć instrukcję za pomocąStatement.RETURN_GENERATED_KEYS aby powiadomić sterownik JDBC o zwrocie 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.");
            }
        }
    }
}

Pamiętaj, że zależy od sterownika JDBC, czy działa. Obecnie większość ostatnich wersji będzie działać, ale jeśli mam rację, sterownik Oracle JDBC nadal jest z tym nieco kłopotliwy. MySQL i DB2 już go obsługiwały od wieków. PostgreSQL zaczął go obsługiwać niedawno. Nie mogę komentować MSSQL, ponieważ nigdy go nie używałem.

Dla Oracle, można wywołać CallableStatementz RETURNINGklauzulą lub SELECT CURRVAL(sequencename)(lub cokolwiek DB-specyficznej składni, aby zrobić) bezpośrednio po INSERTw tej samej transakcji do uzyskania ostatniego wygenerowanego klucza. Zobacz także tę odpowiedź .

BalusC
źródło
4
Lepiej jest pobrać następną wartość w sekwencji przed wstawką, niż uzyskać currval po wstawieniu, ponieważ ta ostatnia może zwrócić niepoprawną wartość w środowisku wielowątkowym (np. W dowolnym kontenerze aplikacji WWW). Sterownik JTDS MSSQL obsługuje getGeneratedKeys.
JeeBee,
4
(powinien wyjaśnić, że zwykle używam Oracle, więc mam bardzo niskie oczekiwania co do możliwości sterownika JDBC).
JeeBee,
7
Ciekawym efektem ubocznym NIE ustawiania opcji Statement.RETURN_GENERATED_KEYS jest komunikat o błędzie, który jest całkowicie niejasny „Instrukcja musi zostać wykonana przed uzyskaniem jakichkolwiek wyników”.
Chris Winters
7
Te generatedKeys.next()powroty trueJeżeli DB zwrócił wygenerowanego klucza. Spójrz, to jest ResultSet. To close()po prostu uwolnić zasoby. W przeciwnym razie w bazie danych zabraknie ich na dłuższą metę, a aplikacja ulegnie awarii. Musisz tylko napisać jakąś metodę narzędzia, która wykonuje zadanie zamykania. Zobacz także to i odpowiedź.
BalusC,
5
Prawidłowa odpowiedź dla większości baz danych / sterowników. W przypadku Oracle nie działa to jednak. W przypadku Oracle zmień na: connection.prepareStatement (sql, nowy String [] {„Nazwa kolumny PK”});
Darrell Teague,
24
  1. Utwórz kolumnę wygenerowaną

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

    PreparedStatement stmtInsert = conn.prepareStatement(insertSQL, generatedColumns);
  3. Użyj ResultSetobiektu, aby pobrać GeneratedKeys na wyciągu

    ResultSet rs = stmtInsert.getGeneratedKeys();
    
    if (rs.next()) {
        long id = rs.getLong(1);
        System.out.println("Inserted ID -" + id); // display inserted record
    }
Surowe Maheswari
źródło
8

Uderzam w Microsoft SQL Server 2008 R2 z jednowątkowej aplikacji opartej na JDBC i ściągam 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 post na blogu ładnie izoluje trzy główne opcje „ostatniego identyfikatora” programu SQL Server: http://msjawahar.wordpress.com/2008/01/25/how-to-find-the-last-identity-value-inserted-in-the -sql-server / - nie potrzebował jeszcze dwóch pozostałych.

ftexperts
źródło
4
To, że aplikacja ma tylko jeden wątek, nie wyklucza warunków wyścigu: jeśli dwóch klientów wstawi wiersz i pobierze identyfikator za pomocą metody, może się nie powieść.
11684
Dlaczego miałbyś? Cieszę się, że nie jestem biednym drańcem, który musi debugować Twój kod, dopuszczając wiele wątków!
mjaggard
6

W przypadku wystąpienia błędu „Nieobsługiwana funkcja” podczas korzystania Statement.RETURN_GENERATED_KEYS, spróbuj wykonać następujące czynności:

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 BATCHIDjest automatycznie wygenerowany identyfikator.

Eitan Rimon
źródło
masz na myśliBATCHID
MoolsBytheway
Świetna odpowiedź!!!
Hasitha Jayawardana
3

Używam SQLServer 2008, ale mam ograniczenie programistyczne: 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 ”).

Właśnie dlatego rozwiązanie conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)rzuciło mi java.lang.AbstractMethodError . W tej sytuacji możliwym rozwiązaniem, które znalazłem, jest stare sugerowane przez Microsoft: Jak odzyskać @@ TOŻSAMOŚĆ Wartość za pomocą 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 działało dla mnie!

Mam nadzieję, że to pomoże!

xanblax
źródło
1

Możesz użyć następującego kodu java, aby uzyskać nowy wstawiony identyfikator.

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);
}
SM
źródło
0

Zamiast komentarza chcę tylko odpowiedzieć na post.


Interfejs java.sql.PreparedStatement

  1. columnIndexes «Możesz użyć funkcji preparStatement, która akceptuje kolumnęIndexes i instrukcję SQL. Tam, gdzie dozwolone są stałe kolumny columnIndexes, to Statement.RETURN_GENERATED_KEYS 1 lub Statement.NO_GENERATED_KEYS [2], instrukcja SQL, która może zawierać co najmniej jedno „?” Symbole zastępcze parametrów IN.

    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. columnNames « Wymień kolumny, takie jak 'id', 'uniqueID', .... w tabeli docelowej zawierającej automatycznie wygenerowane klucze, które powinny zostać zwrócone. Sterownik je zignoruje, jeśli instrukcja SQL nie jest INSERTinstrukcją.

    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();
    }
}
Yash
źródło
0

Dzięki NativeQuery Hibernata musisz zwrócić ResultList zamiast SingleResult, ponieważ Hibernacja modyfikuje natywne zapytanie

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

lubić

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

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

Balin
źródło
0

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.");
  }
}
rogerdpack
źródło
0

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);
}
TheSagya
źródło
Nadal uważam, że jest to długie podejście, aby je zdobyć. Myślę, że będzie też bardziej skompresowane rozwiązanie.
TheSagya
-6
Connection cn = DriverManager.getConnection("Host","user","pass");
Statement st = cn.createStatement("Ur Requet Sql");
int ret  = st.execute();
Abdelkhalek Benhoumine
źródło
Przepraszam, ale co to ma być?
MoolsBytheway,
1. createStatementMetoda Connectionnie przewiduje żadnych parametrów. 2. executeMetoda od Statementoczekuje ciągu z zapytaniem. 3. executeMetoda zwraca: truejeśli pierwszym wynikiem jest ResultSetobiekt; falsejeśli jest to liczba aktualizacji lub nie ma żadnych wyników. docs.oracle.com/javase/7/docs/api/java/sql/…
atilacamurca