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?
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:
publicvoid create(User user)throwsSQLException{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){thrownewSQLException("Creating user failed, no rows affected.");}try(ResultSet generatedKeys = statement.getGeneratedKeys()){if(generatedKeys.next()){
user.setId(generatedKeys.getLong(1));}else{thrownewSQLException("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ź .
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 tę 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
Utwórz kolumnę wygenerowaną
String generatedColumns[]={"ID"};
Przekaż tę genowaną kolumnę do swojego oświadczenia
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:
privateint insertQueryReturnInt(StringSQLQy){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;}
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){thrownewSQLException("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.
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.*;publicclassIdentitySample{publicstaticvoid 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 DriverClass.forName("com.microsoft.jdbc.sqlserver.SQLServerDriver").newInstance();//Connect to SQL ServerConnection 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 statementCallableStatement 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 resultsetswhile(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 @@IDENTITYif(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){}}}
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.
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ą.
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).
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/…
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); }
Odpowiedzi:
Jeśli jest to klucz generowany automatycznie, możesz
Statement#getGeneratedKeys()
do tego użyć . Musisz wywołać go tak samo,Statement
jak ten używany wINSERT
. Najpierw musisz utworzyć instrukcję za pomocąStatement.RETURN_GENERATED_KEYS
aby powiadomić sterownik JDBC o zwrocie kluczy.Oto podstawowy przykład:
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ć
CallableStatement
zRETURNING
klauzulą lubSELECT CURRVAL(sequencename)
(lub cokolwiek DB-specyficznej składni, aby zrobić) bezpośrednio poINSERT
w tej samej transakcji do uzyskania ostatniego wygenerowanego klucza. Zobacz także tę odpowiedź .źródło
generatedKeys.next()
powrotytrue
Jeżeli DB zwrócił wygenerowanego klucza. Spójrz, to jestResultSet
. Toclose()
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 tę odpowiedź.Utwórz kolumnę wygenerowaną
Przekaż tę genowaną kolumnę do swojego oświadczenia
Użyj
ResultSet
obiektu, aby pobrać GeneratedKeys na wyciąguźródło
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:
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.
źródło
W przypadku wystąpienia błędu „Nieobsługiwana funkcja” podczas korzystania
Statement.RETURN_GENERATED_KEYS
, spróbuj wykonać następujące czynności:Gdzie
BATCHID
jest automatycznie wygenerowany identyfikator.źródło
BATCHID
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ą JDBCTo rozwiązanie działało dla mnie!
Mam nadzieję, że to pomoże!
źródło
Możesz użyć następującego kodu java, aby uzyskać nowy wstawiony identyfikator.
źródło
Zamiast komentarza chcę tylko odpowiedzieć na post.
Interfejs java.sql.PreparedStatement
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 «
Przykład:
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 jestINSERT
instrukcją.SKŁADNIA «
Przykład:
Pełny przykład:
źródło
Dzięki NativeQuery Hibernata musisz zwrócić ResultList zamiast SingleResult, ponieważ Hibernacja modyfikuje natywne zapytanie
lubić
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).
źródło
Można go używać również z normalnymi
Statement
(nie tylkoPreparedStatement
)źródło
W moim przypadku ->
źródło
Jeśli używasz Spring JDBC, możesz użyć klasy GeneratedKeyHolder Springa, aby uzyskać wstawiony identyfikator.
Zobacz tę odpowiedź ... Jak uzyskać wstawiony identyfikator za pomocą Spring Jdbctemplate.update (String sql, obj ... args)
źródło
źródło
createStatement
MetodaConnection
nie przewiduje żadnych parametrów. 2.execute
Metoda odStatement
oczekuje ciągu z zapytaniem. 3.execute
Metoda zwraca:true
jeśli pierwszym wynikiem jestResultSet
obiekt;false
jeśli jest to liczba aktualizacji lub nie ma żadnych wyników. docs.oracle.com/javase/7/docs/api/java/sql/…