Eu quero INSERTum registro em um banco de dados (que é o Microsoft SQL Server no meu caso) usando JDBC em Java. Ao mesmo tempo, quero obter o ID da inserção. Como conseguir isso usando a API JDBC?
Deixe o ID AutoGenrerated na consulta 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
Respostas:
650
Se for uma chave gerada automaticamente, você poderá usá Statement#getGeneratedKeys()-la. Você precisa chamá-lo da mesma Statementforma que está sendo usado para o INSERT. Você primeiro precisa criar a instrução usando Statement.RETURN_GENERATED_KEYSpara notificar o driver JDBC para retornar as chaves.
Aqui está um exemplo básico:
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.");}}}}
Observe que você depende do driver JDBC para saber se ele funciona. Atualmente, a maioria das últimas versões funcionará, mas se eu estiver correto, o driver Oracle JDBC ainda é um pouco problemático. O MySQL e o DB2 já o suportam há séculos. O PostgreSQL começou a apoiá-lo há pouco tempo. Não posso comentar sobre o MSSQL como nunca o usei.
Para Oracle, você pode chamar a CallableStatementcom uma RETURNINGcláusula ou uma SELECT CURRVAL(sequencename)(ou qualquer sintaxe específica do DB para fazer isso) diretamente após INSERTa mesma transação para obter a última chave gerada. Veja também esta resposta .
É melhor obter o próximo valor em uma sequência antes da inserção do que obter o currval após a inserção, porque o último pode retornar o valor errado em um ambiente multithread (por exemplo, qualquer contêiner de aplicativo da web). O driver JTDS MSSQL suporta getGeneratedKeys.
21710 JeeBee
4
(deve esclarecer que eu normalmente uso Oracle, portanto, tenho expectativas muito baixas dos recursos de um driver JDBC normalmente).
JeeBee
7
Um efeito colateral interessante de NÃO definir a opção Statement.RETURN_GENERATED_KEYS é a mensagem de erro, que é completamente obscura "A instrução deve ser executada antes que quaisquer resultados possam ser obtidos."
Chris Winters
7
Os generatedKeys.next()retornos truese o DB devolvida uma chave gerada. Olha, é um ResultSet. O close()é apenas para liberar recursos. Caso contrário, seu banco de dados ficará sem eles a longo prazo e seu aplicativo será interrompido. Você só precisa escrever algum método utilitário que execute a tarefa de fechamento. Veja também esta e esta resposta.
BalusC
5
Resposta correta para a maioria dos bancos de dados / drivers. Para Oracle, isso não funciona no entanto. Para Oracle, altere para: connection.prepareStatement (sql, new String [] {"PK column name"});
Estou acessando o Microsoft SQL Server 2008 R2 a partir de um aplicativo baseado em JDBC de thread único e retirando o último ID sem usar a propriedade RETURN_GENERATED_KEYS ou qualquer PreparedStatement. Parece algo assim:
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;}
O fato de o aplicativo ter apenas um encadeamento não impossibilita uma condição de corrida: se dois clientes inserem uma linha e recuperam o ID com seu método, isso pode falhar.
11684
Por que você? Estou feliz por não ser o pobre coitado que precisa depurar seu código ao permitir vários threads!
Mjaggard #
6
Ao encontrar um erro 'Recurso não suportado' durante o uso Statement.RETURN_GENERATED_KEYS, tente o seguinte:
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();}
Estou usando o SQLServer 2008, mas tenho uma limitação de desenvolvimento: não consigo usar um novo driver, tenho que usar "com.microsoft.jdbc.sqlserver.SQLServerDriver" (não consigo usar "com.microsoft.sqlserver.jdbc .SQLServerDriver ").
É por isso que a solução conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)lançou um java.lang.AbstractMethodError para mim. Nessa situação, uma solução possível que encontrei é a antiga sugerida pela Microsoft:
Como recuperar o valor @@ IDENTITY usando 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 «Você pode usar a função prepareStatement que aceita columnIndexes e instrução SQL.
Onde columnIndexes permitidos sinalizadores constantes são Statement.RETURN_GENERATED_KEYS 1 ou Statement.NO_GENERATED_KEYS [2], instrução SQL que pode conter um ou mais '?' Marcadores de parâmetro IN.
columnNames « Liste os columnNames como 'id', 'uniqueID', .... na tabela de destino que contém as chaves geradas automaticamente que devem ser retornadas. O driver irá ignorá-los se a instrução SQL não for uma INSERTinstrução.
Com o NativeQuery do Hibernate, você precisa retornar um ResultList em vez de um SingleResult, porque o Hibernate modifica uma consulta nativa
INSERT INTO bla (a,b) VALUES (2,3) RETURNING id
gostar
INSERT INTO bla (a,b) VALUES (2,3) RETURNING id LIMIT 1
se você tentar obter um único resultado, o que fará com que a maioria dos bancos de dados (pelo menos PostgreSQL) gere um erro de sintaxe. Depois, você pode buscar o ID resultante da lista (que geralmente contém exatamente um item).
1. O createStatementmétodo de Connectionnão espera nenhum parâmetro. 2. O executemétodo de Statementespera uma String com uma Consulta. 3. O executemétodo retorna: truese o primeiro resultado for um ResultSetobjeto; falsese é uma contagem de atualização ou não há resultados. 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); }
Respostas:
Se for uma chave gerada automaticamente, você poderá usá
Statement#getGeneratedKeys()
-la. Você precisa chamá-lo da mesmaStatement
forma que está sendo usado para oINSERT
. Você primeiro precisa criar a instrução usandoStatement.RETURN_GENERATED_KEYS
para notificar o driver JDBC para retornar as chaves.Aqui está um exemplo básico:
Observe que você depende do driver JDBC para saber se ele funciona. Atualmente, a maioria das últimas versões funcionará, mas se eu estiver correto, o driver Oracle JDBC ainda é um pouco problemático. O MySQL e o DB2 já o suportam há séculos. O PostgreSQL começou a apoiá-lo há pouco tempo. Não posso comentar sobre o MSSQL como nunca o usei.
Para Oracle, você pode chamar a
CallableStatement
com umaRETURNING
cláusula ou umaSELECT CURRVAL(sequencename)
(ou qualquer sintaxe específica do DB para fazer isso) diretamente apósINSERT
a mesma transação para obter a última chave gerada. Veja também esta resposta .fonte
generatedKeys.next()
retornostrue
se o DB devolvida uma chave gerada. Olha, é umResultSet
. Oclose()
é apenas para liberar recursos. Caso contrário, seu banco de dados ficará sem eles a longo prazo e seu aplicativo será interrompido. Você só precisa escrever algum método utilitário que execute a tarefa de fechamento. Veja também esta e esta resposta.Criar coluna gerada
Passe esta coluna geneada à sua declaração
Use o
ResultSet
objeto para buscar as GeneratedKeys on Statementfonte
Estou acessando o Microsoft SQL Server 2008 R2 a partir de um aplicativo baseado em JDBC de thread único e retirando o último ID sem usar a propriedade RETURN_GENERATED_KEYS ou qualquer PreparedStatement. Parece algo assim:
Esta postagem no blog isola bem três opções principais de "última identificação" do SQL Server: http://msjawahar.wordpress.com/2008/01/25/how-to-find-the-last-identity-value-inserted-in-the -sql-server / - ainda não precisou dos outros dois.
fonte
Ao encontrar um erro 'Recurso não suportado' durante o uso
Statement.RETURN_GENERATED_KEYS
, tente o seguinte:Onde
BATCHID
está o ID gerado automaticamente.fonte
BATCHID
Estou usando o SQLServer 2008, mas tenho uma limitação de desenvolvimento: não consigo usar um novo driver, tenho que usar "com.microsoft.jdbc.sqlserver.SQLServerDriver" (não consigo usar "com.microsoft.sqlserver.jdbc .SQLServerDriver ").
É por isso que a solução
conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)
lançou um java.lang.AbstractMethodError para mim. Nessa situação, uma solução possível que encontrei é a antiga sugerida pela Microsoft: Como recuperar o valor @@ IDENTITY usando JDBCEsta solução funcionou para mim!
Eu espero que isso ajude!
fonte
Em vez de um comentário , só quero responder a postagem.
Interface java.sql.PreparedStatement
columnIndexes «Você pode usar a função prepareStatement que aceita columnIndexes e instrução SQL. Onde columnIndexes permitidos sinalizadores constantes são Statement.RETURN_GENERATED_KEYS 1 ou Statement.NO_GENERATED_KEYS [2], instrução SQL que pode conter um ou mais '?' Marcadores de parâmetro IN.
SYNTAX «
Exemplo:
columnNames « Liste os columnNames como
'id', 'uniqueID', ...
. na tabela de destino que contém as chaves geradas automaticamente que devem ser retornadas. O driver irá ignorá-los se a instrução SQL não for umaINSERT
instrução.SYNTAX «
Exemplo:
Exemplo completo:
fonte
Você pode usar o seguinte código java para obter o novo ID inserido.
fonte
Com o NativeQuery do Hibernate, você precisa retornar um ResultList em vez de um SingleResult, porque o Hibernate modifica uma consulta nativa
gostar
se você tentar obter um único resultado, o que fará com que a maioria dos bancos de dados (pelo menos PostgreSQL) gere um erro de sintaxe. Depois, você pode buscar o ID resultante da lista (que geralmente contém exatamente um item).
fonte
É possível usá-lo com o normal
Statement
também (não apenasPreparedStatement
)fonte
No meu caso ->
fonte
Se você estiver usando o Spring JDBC, poderá usar a classe GeneratedKeyHolder do Spring para obter o ID inserido.
Veja esta resposta ... Como obter a ID inserida usando Spring Jdbctemplate.update (String sql, obj ... args)
fonte
fonte
createStatement
método deConnection
não espera nenhum parâmetro. 2. Oexecute
método deStatement
espera uma String com uma Consulta. 3. Oexecute
método retorna:true
se o primeiro resultado for umResultSet
objeto;false
se é uma contagem de atualização ou não há resultados. docs.oracle.com/javase/7/docs/api/java/sql/…