Quiero INSERTun registro en una base de datos (que es Microsoft SQL Server en mi caso) usando JDBC en Java. Al mismo tiempo, quiero obtener la ID de inserción. ¿Cómo puedo lograr esto usando la API JDBC?
Deje la identificación que está AutoGenrerated en la 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
Respuestas:
650
Si se trata de una clave generada automáticamente, puede usarla Statement#getGeneratedKeys()para esto. Debe llamarlo igual Statementque el que se está utilizando para INSERT. Primero debe crear la declaración utilizando Statement.RETURN_GENERATED_KEYSpara notificar al controlador JDBC que devuelva las claves.
Aquí hay un ejemplo 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.");}}}}
Tenga en cuenta que depende del controlador JDBC para saber si funciona. Actualmente, la mayoría de las últimas versiones funcionarán, pero si estoy en lo cierto, el controlador Oracle JDBC sigue siendo un poco problemático con esto. MySQL y DB2 ya lo soportaron por años. PostgreSQL comenzó a admitirlo no hace mucho tiempo. No puedo comentar sobre MSSQL ya que nunca lo he usado.
Para Oracle, puede invocar a CallableStatementcon una RETURNINGcláusula o un SELECT CURRVAL(sequencename)(o cualquier sintaxis específica de DB para hacerlo) directamente después INSERTde la misma transacción para obtener la última clave generada. Ver también esta respuesta .
Es mejor obtener el siguiente valor en una secuencia antes de la inserción que obtener el currval después de la inserción, ya que este último puede devolver el valor incorrecto en un entorno de subprocesos múltiples (por ejemplo, cualquier contenedor de aplicaciones web). El controlador JTDS MSSQL admite getGeneratedKeys.
JeeBee
44
(Debería aclarar que generalmente uso Oracle, por lo que normalmente tengo muy pocas expectativas de las capacidades de un controlador JDBC).
JeeBee
77
Un efecto secundario interesante de NO establecer la opción Statement.RETURN_GENERATED_KEYS es el mensaje de error, que es completamente oscuro "La declaración debe ejecutarse antes de que se puedan obtener resultados".
Chris Winters
77
Los generatedKeys.next()rendimientos truesi el DB devolvió una clave generada. Mira, es un ResultSet. El close()es solo para liberar recursos. De lo contrario, su base de datos se quedará sin ellos a largo plazo y su aplicación se romperá. Solo tiene que escribir algún método de utilidad que haga la tarea de cierre. Vea también esto y esta respuesta.
BalusC
55
Respuesta correcta para la mayoría de las bases de datos / controladores. Sin embargo, para Oracle esto no funciona. Para Oracle, cambie a: connection.prepareStatement (sql, new String [] {"PK column name"});
Estoy accediendo a Microsoft SQL Server 2008 R2 desde una aplicación basada en JDBC de un solo subproceso y retirando el último ID sin usar la propiedad RETURN_GENERATED_KEYS o cualquier PreparedStatement. Se ve algo como esto:
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;}
Que la aplicación tenga solo un subproceso no hace imposible una condición de carrera: si dos clientes insertan una fila y recuperan la ID con su método, puede fallar.
11684
¿Por que lo harias? ¡Me alegra no ser el pobre idiota que tiene que depurar su código cuando permite múltiples hilos!
mjaggard
6
Cuando encuentre un error de 'Función no compatible' mientras lo usa Statement.RETURN_GENERATED_KEYS, intente esto:
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();}
¿Dónde BATCHIDestá la identificación generada automáticamente?
Estoy usando SQLServer 2008, pero tengo una limitación de desarrollo: no puedo usar un nuevo controlador, tengo que usar "com.microsoft.jdbc.sqlserver.SQLServerDriver" (No puedo usar "com.microsoft.sqlserver.jdbc .SQLServerDriver ").
Es por eso que la solución conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)arrojó un java.lang.AbstractMethodError para mí. En esta situación, una posible solución que encontré es la anterior sugerida por Microsoft:
Cómo recuperar el valor de @@ IDENTITY utilizando 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 «Puede usar la función prepareStatement que acepta columnIndexes y la declaración SQL.
Donde columnIndexes permitieron indicadores constantes son Statement.RETURN_GENERATED_KEYS 1 o Statement.NO_GENERATED_KEYS [2], instrucción SQL que puede contener uno o más '?' Marcadores de posición de parámetros IN.
columnNames « Listar los columnNames como 'id', 'uniqueID', .... en la tabla de destino que contiene las claves generadas automáticamente que deben devolverse. El controlador los ignorará si la declaración SQL no es una INSERTdeclaración.
Con NativeQuery de Hibernate, debe devolver una lista de resultados en lugar de un resultado único, porque Hibernate modifica una consulta nativa
INSERT INTO bla (a,b) VALUES (2,3) RETURNING id
me gusta
INSERT INTO bla (a,b) VALUES (2,3) RETURNING id LIMIT 1
si intenta obtener un único resultado, lo que hace que la mayoría de las bases de datos (al menos PostgreSQL) arrojen un error de sintaxis. Luego, puede buscar la identificación resultante de la lista (que generalmente contiene exactamente un elemento).
1. El createStatementmétodo de Connectionno espere ningún parámetro. 2. El executemétodo de Statementespera una cadena con una consulta. 3. El executemétodo devuelve: truesi el primer resultado es un ResultSetobjeto; falsesi es un recuento de actualizaciones o no hay 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); }
Respuestas:
Si se trata de una clave generada automáticamente, puede usarla
Statement#getGeneratedKeys()
para esto. Debe llamarlo igualStatement
que el que se está utilizando paraINSERT
. Primero debe crear la declaración utilizandoStatement.RETURN_GENERATED_KEYS
para notificar al controlador JDBC que devuelva las claves.Aquí hay un ejemplo básico:
Tenga en cuenta que depende del controlador JDBC para saber si funciona. Actualmente, la mayoría de las últimas versiones funcionarán, pero si estoy en lo cierto, el controlador Oracle JDBC sigue siendo un poco problemático con esto. MySQL y DB2 ya lo soportaron por años. PostgreSQL comenzó a admitirlo no hace mucho tiempo. No puedo comentar sobre MSSQL ya que nunca lo he usado.
Para Oracle, puede invocar a
CallableStatement
con unaRETURNING
cláusula o unSELECT CURRVAL(sequencename)
(o cualquier sintaxis específica de DB para hacerlo) directamente despuésINSERT
de la misma transacción para obtener la última clave generada. Ver también esta respuesta .fuente
generatedKeys.next()
rendimientostrue
si el DB devolvió una clave generada. Mira, es unResultSet
. Elclose()
es solo para liberar recursos. De lo contrario, su base de datos se quedará sin ellos a largo plazo y su aplicación se romperá. Solo tiene que escribir algún método de utilidad que haga la tarea de cierre. Vea también esto y esta respuesta.Crear columna generada
Pase esta columna generada a su estado de cuenta
Use el
ResultSet
objeto para obtener las claves generadas en la instrucciónfuente
Estoy accediendo a Microsoft SQL Server 2008 R2 desde una aplicación basada en JDBC de un solo subproceso y retirando el último ID sin usar la propiedad RETURN_GENERATED_KEYS o cualquier PreparedStatement. Se ve algo como esto:
Esta publicación de blog aísla muy bien tres opciones principales de "último ID" de SQL Server: http://msjawahar.wordpress.com/2008/01/25/how-to-find-the-last-identity-value-inserted-in-the -sql-server / - aún no he necesitado los otros dos.
fuente
Cuando encuentre un error de 'Función no compatible' mientras lo usa
Statement.RETURN_GENERATED_KEYS
, intente esto:¿Dónde
BATCHID
está la identificación generada automáticamente?fuente
BATCHID
Estoy usando SQLServer 2008, pero tengo una limitación de desarrollo: no puedo usar un nuevo controlador, tengo que usar "com.microsoft.jdbc.sqlserver.SQLServerDriver" (No puedo usar "com.microsoft.sqlserver.jdbc .SQLServerDriver ").
Es por eso que la solución
conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)
arrojó un java.lang.AbstractMethodError para mí. En esta situación, una posible solución que encontré es la anterior sugerida por Microsoft: Cómo recuperar el valor de @@ IDENTITY utilizando JDBC¡Esta solución funcionó para mí!
¡Espero que esto ayude!
fuente
En lugar de un comentario , solo quiero responder la publicación.
Interfaz java.sql.PreparedStatement
columnIndexes «Puede usar la función prepareStatement que acepta columnIndexes y la declaración SQL. Donde columnIndexes permitieron indicadores constantes son Statement.RETURN_GENERATED_KEYS 1 o Statement.NO_GENERATED_KEYS [2], instrucción SQL que puede contener uno o más '?' Marcadores de posición de parámetros IN.
SINTAXIS «
Ejemplo:
columnNames « Listar los columnNames como
'id', 'uniqueID', ...
. en la tabla de destino que contiene las claves generadas automáticamente que deben devolverse. El controlador los ignorará si la declaración SQL no es unaINSERT
declaración.SINTAXIS «
Ejemplo:
Ejemplo completo:
fuente
Puede usar el siguiente código Java para obtener una nueva identificación insertada.
fuente
Con NativeQuery de Hibernate, debe devolver una lista de resultados en lugar de un resultado único, porque Hibernate modifica una consulta nativa
me gusta
si intenta obtener un único resultado, lo que hace que la mayoría de las bases de datos (al menos PostgreSQL) arrojen un error de sintaxis. Luego, puede buscar la identificación resultante de la lista (que generalmente contiene exactamente un elemento).
fuente
Es posible usarlo también con normales
Statement
(no soloPreparedStatement
)fuente
En mi caso ->
fuente
Si está utilizando Spring JDBC, puede utilizar la clase GeneratedKeyHolder de Spring para obtener la ID insertada.
Vea esta respuesta ... Cómo insertar la identificación usando Spring Jdbctemplate.update (String sql, obj ... args)
fuente
fuente
createStatement
método deConnection
no espere ningún parámetro. 2. Elexecute
método deStatement
espera una cadena con una consulta. 3. Elexecute
método devuelve:true
si el primer resultado es unResultSet
objeto;false
si es un recuento de actualizaciones o no hay resultados. docs.oracle.com/javase/7/docs/api/java/sql/…