2009-06-16 3 views
9

Existe-t-il un moyen d'insérer une ligne dans une table et d'obtenir le nouvel ID généré, en une seule instruction? Je veux utiliser JDBC, et l'ID sera généré par une séquence ou sera un champ d'auto-incrémentation.Obtention de l'ID d'une insertion dans la même instruction

Merci pour votre aide.

John Pollancre

+0

Pouvez-vous donner un exemple de ce que vous voulez faire, en supposant qu'il était possible ? – skaffman

+0

Bien sûr. Considérons void table A (id [autoincrement], champ). Je voudrais quelque chose comme: long id = jdbcStatement.execute ("insérer dans A (field) values ​​('blah')"); Long id = jdbcStatement.execute ("insérer dans A (id, field) values ​​(sequence.nextval, 'blah')"); Je veux id = 1, et tout fait en un seul accès à la base de données –

+0

+1 Bonne question et utile lors de l'insertion d'objets qui sont liés du côté de Java. –

Répondre

0

La valeur de l'ID généré automatiquement ne sait pas qu'après l'INSERT est exécutée, parce que d'autres déclarations pourraient être en même temps et le exécutaient SGBDR va décider comment planifier lequel commence.

Toute fonction que vous appelez dans une expression de l'instruction INSERT doit être évaluée avant l'insertion de la nouvelle ligne et, par conséquent, elle ne peut pas savoir quelle valeur d'ID est générée.

je peux penser à deux options qui sont proches de ce que vous demandez:

  • écrire un trigger qui court après INSERT, donc vous avez accès à la valeur de clé ID générée. Ecrivez une procédure pour envelopper l'insertion, de sorte que vous puissiez exécuter un autre code dans la procédure et interroger le dernier ID généré.

Cependant, je soupçonne que ce que vous demandez vraiment est de savoir si vous pouvez interroger la dernière valeur d'ID généré par session en cours, même si d'autres sessions insèrent également des lignes et de générer leurs propres valeurs d'identité. Vous pouvez être assuré que chaque SGBDR qui offre une fonction d'auto-incrémentation offre un moyen d'interroger cette valeur, et il vous indique le dernier ID généré dans votre portée de session actuelle. Ceci n'est pas affecté par les insertions effectuées dans d'autres sessions.

+0

"... parce que d'autres instructions pourraient être en cours d'exécution en même temps ..." Je me demandais à ce sujet. Pourquoi ne pas synchroniser les méthodes en Java pour éviter cela? –

+0

Vous pouvez sérialiser les transactions soit en établissant des sections critiques dans le code de l'application, soit en demandant des verrous de table explicitement, mais il n'en reste pas moins que SQL n'a pas de syntaxe pour demander l'ID dans la même instruction. –

10

utilisant getGeneratedKeys():

resultSet = pstmt.getGeneratedKeys(); 

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

Terrific! Je n'en ai jamais entendu parler! Pensez-vous que cela fonctionne avec les identifiants générés par une séquence? Merci, dfa! –

+0

juste essayer :-) – dfa

+1

Les pilotes Oracle JDBC retournent le ROWID pour getGeneratedKeys, car Oracle n'a pas le concept réel d'une clé générée automatiquement - il a des séquences, mais vous devez les utiliser explicitement pour remplir le champ soit dans votre INSERT, soit dans un trigger. J'utiliserais la méthode RETURNING décrite par Vincent. –

0

l'identifiant généré par une séquence peuvent être obtenus par l'intermédiaire d'

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

couru dans la même opération que pour obtenir une vue cohérente.

+0

Ceci n'est pas sûr pour les threads, comme le souligne Robert M ci-dessous. – massfords

6

Vous pouvez utiliser la clause RETURNING pour obtenir la valeur de toute colonne que vous avez mise à jour ou insérée. Cela fonctionne avec le trigger (i-e: vous obtenez les valeurs réellement insérées après l'exécution des triggers). Considérez:

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

Je pense que vous trouverez ce utile:

J'ai une table avec un id auto-incrémentée. De temps à fois je veux insérer des lignes à cette table , mais je veux être en mesure de savoir ce que le pk de la ligne nouvellement inséré est.

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)); 

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

0

Je ne pouvais pas commenter, sinon je viens d'ajouter au poste de DFA, mais ce qui suit est un exemple de cette fonctionnalité avec JDBC droite.

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

Cependant, si vous utilisez quelque chose comme printemps, ils masquent beaucoup de détails gores pour vous. Si cela peut être de toute aide, il suffit de bien ressortir le chapitre 11, qui est les détails JDBC. L'utiliser m'a sauvé beaucoup de maux de tête.

1

Je ne pouvais pas commenter sinon je l'aurais ajouté au poste 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. 

Mise à jour séquence de nextval après avoir obtenu un de c'est une opération autonome. Sinon, une autre session obtiendrait la même valeur de la séquence. Donc obtenir currval n'obtiendra pas l'id inséré si une autre session a été sélectionnée dans la séquence entre l'insertion et la sélection.

Cordialement, Rob

+0

Qu'en est-il des transactions imbriquées? (Pas un expert Oracle, alors s'il vous plaît excuser des questions stupides) –

3

En fait, je pense que nextval suivie currval fonctionne. Voici un peu de code qui simule ce comportement avec deux threads, un qui fait d'abord un nextval, puis un currval, tandis qu'un second thread fait un nextval entre les deux.

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(); 
} 

Le résultat est le suivant: Session1 nextval est: 47 Session2 nextval est: 48 Session1 currval est: 47

Questions connexes