2009-06-16 29 views
9

¿Hay alguna manera de insertar una fila en una tabla y obtener la nueva ID generada, en una sola declaración? Quiero usar JDBC, y el ID será generado por una secuencia o será un campo de autoincrement.Obtener la identificación de una inserción en la misma declaración

Gracias por su ayuda.

John Pollancre

+0

¿Puede dar un ejemplo de lo que quiere hacer, suponiendo que fuera posible ? – skaffman

+0

Por supuesto. Considere la tabla vacía A (id [autoincrement], campo). Me gustaría algo como: long id = jdbcStatement.execute ("insertar en A (campo) values ​​('blah')"); o ID largo = jdbcStatement.execute ("insertar en A (id, campo) valores (sequence.nextval, 'blah')"); Quiero id = 1, y todo hecho en un solo acceso a DB –

+0

+1 Buena pregunta y útil al insertar objetos que están vinculados por Java. –

Respuesta

0

El valor del ID autogenerado no se conoce hasta después de que se ejecute la instrucción INSERT, ya que otros estados podrían ejecutando al mismo tiempo y el RDBMS puede decidir cómo programar cuál va primero.

Cualquier función que llame en una expresión en la instrucción INSERT deberá evaluarse antes de insertar la nueva fila y, por lo tanto, no puede saber qué valor de ID se genera.

puedo pensar en dos opciones que están cerca de lo que preguntas:

  • para tu dispositivo disparador que se ejecuta después de INSERT, por lo que tiene acceso al valor de clave de identificación generado.

  • Escriba un procedimiento para envolver la inserción, para que pueda ejecutar otro código en el procedimiento y consultar la última ID generada.

Sin embargo, sospecho que lo que realmente está pidiendo es si se puede consultar por el último valor de ID generado por la sesión actual aunque otras sesiones también están insertando filas y generar sus propios valores de ID. Puede estar seguro de que cada RDBMS que ofrece un servicio de autoincremento ofrece una forma de consultar este valor, y le informa la última ID generada en su ámbito de sesión actual. Esto no se ve afectado por las inserciones realizadas en otras sesiones.

+0

"... porque otras declaraciones podrían estar ejecutándose al mismo tiempo ..." Me estaba preguntando sobre eso. ¿Qué hay de la sincronización de métodos en Java para evitar esto? –

+0

Puede serializar las transacciones ya sea estableciendo secciones críticas en el código de la aplicación o solicitando bloqueos de tabla explícitamente, pero el hecho es que SQL no tiene ninguna sintaxis para solicitar el ID en la misma declaración. –

10

utilizando getGeneratedKeys():

resultSet = pstmt.getGeneratedKeys(); 

if (resultSet != null && resultSet.next()) { 
    lastId = resultSet.getInt(1); 
} 
+0

¡Terrific! ¡Nunca antes lo había escuchado! ¿Crees que funciona con identificadores generados por una secuencia? Gracias, dfa! –

+0

simplemente pruébelo :-) – dfa

+1

Los controladores Oracle JDBC devuelven el ROWID para getGeneratedKeys, porque Oracle no tiene el concepto real de una clave generada automáticamente: tiene secuencias, pero tiene que usarlas explícitamente para rellenar el campo ya sea en tu INSERT o en un disparador. Usaría el método de RETORNO descrito por Vincent. –

0

el ID generado por una secuencia se pueden obtener a través de

insert into table values (sequence.NextVal, otherval) 
select sequence.CurrVal 

corrieron en la misma transacción que para obtener una visión consistente.

+0

Esto no es seguro para subprocesos como lo señala Robert M a continuación. – massfords

6

Puede usar la cláusula RETURNING para obtener el valor de cualquier columna que haya actualizado o insertado. Funciona con desencadenante (i-e: obtienes los valores realmente insertados después de la ejecución de desencadenantes). Considere:

SQL> CREATE TABLE a (ID NUMBER PRIMARY KEY); 

Table created 
SQL> CREATE SEQUENCE a_seq; 

Sequence created 
SQL> VARIABLE x NUMBER; 
SQL> BEGIN 
    2  INSERT INTO a VALUES (a_seq.nextval) RETURNING ID INTO :x; 
    3 END; 
    4/

PL/SQL procedure successfully completed 
x 
--------- 
1 

SQL>/

PL/SQL procedure successfully completed 
x 
--------- 
2 
0

creo que te ha resultado útil:

Tengo una tabla con un ID de incremento automático . De tiempo al tiempo quiero insertar filas en esta tabla , pero quiero saber cuál es el pk de la fila recién insertada .

String query = "BEGIN INSERT INTO movement (doc_number) VALUES ('abc') RETURNING id INTO ?; END;"; 
OracleCallableStatement cs = (OracleCallableStatement) conn.prepareCall(query); 
cs.registerOutParameter(1, OracleTypes.NUMBER); 
cs.execute(); 
System.out.println(cs.getInt(1)); 

Fuente: Thread: Oracle/JDBC Error when Returning values from an Insert

0

que no podía comentar, de lo contrario habría acaba de agregar al post de DFA, pero el siguiente es un ejemplo de esta funcionalidad con JDBC recta.

http://www.ibm.com/developerworks/java/library/j-jdbcnew/

Sin embargo, si usted está usando algo como la primavera, van a enmascarar muchos de los detalles morbosos para usted. Si eso puede ser de alguna ayuda, solo es bueno el Capítulo 11 de Spring, que es el detalle de JDBC. Usarlo me ha ahorrado muchos dolores de cabeza.

1

que no podía comentar de lo contrario habría añadido al post de Vinko Vrsalovic:

The id generated by a sequence can be obtained via 

insert into table values (sequence.NextVal, otherval) 
select sequence.CurrVal 

ran in the same transaction as to get a consistent view. 

Actualización de secuencia después de conseguir un nextval de ella es una transacción autónoma. De lo contrario, otra sesión obtendría el mismo valor de la secuencia. Así que obtener currval no obtendrá la identificación insertada si se ha seleccionado otra sesión de la secuencia entre la inserción y la selección.

Saludos, Rob

+0

¿Qué pasa con las transacciones anidadas? (No es un experto en Oracle, así que por favor disculpen las preguntas tontas) –

3

En realidad, creo que nextval seguido por currval funciona. Aquí hay un poco de código que simula este comportamiento con dos hilos, uno que primero hace un nextval, luego un currval, mientras que un segundo thread hace un nextval entre ellos.

public void checkSequencePerSession() throws Exception { 
    final Object semaphore = new Object(); 
    Runnable thread1 = new Runnable() { 
     public void run() { 
      try { 
       Connection con = getConnection(); 
       Statement s = con.createStatement(); 
       ResultSet r = s.executeQuery("SELECT SEQ_INV_BATCH_DWNLD.nextval AS val FROM DUAL "); 
       r.next(); 
       System.out.println("Session1 nextval is: " + r.getLong("val")); 
       synchronized(semaphore){ 
       semaphore.notify(); 
       } 
       synchronized(semaphore){ 
       semaphore.wait(); 
       } 
       r = s.executeQuery("SELECT SEQ_INV_BATCH_DWNLD.currval AS val FROM DUAL "); 
       r.next(); 
       System.out.println("Session1 currval is: " + r.getLong("val")); 
       con.commit(); 
      } catch (Exception e) { 
       e.printStackTrace(); 
      } 
     } 
    }; 
    Runnable thread2 = new Runnable(){ 
     public void run(){ 
      try{ 
       synchronized(semaphore){ 
       semaphore.wait(); 
       } 
       Connection con = getConnection(); 
       Statement s = con.createStatement(); 
       ResultSet r = s.executeQuery("SELECT SEQ_INV_BATCH_DWNLD.nextval AS val FROM DUAL "); 
       r.next(); 
       System.out.println("Session2 nextval is: " + r.getLong("val")); 
       con.commit(); 
       synchronized(semaphore){ 
       semaphore.notify(); 
       } 
      }catch(Exception e){ 
       e.printStackTrace(); 
      } 
     } 
    }; 
    Thread t1 = new Thread(thread1); 
    Thread t2 = new Thread(thread2); 
    t1.start(); 
    t2.start(); 
    t1.join(); 
    t2.join(); 
} 

El resultado es el siguiente: reunión1 nextval es: 47 período de sesiones2 nextval es: 48 reunión1 currval es: 47

Cuestiones relacionadas