2013-07-25 10 views
5

Sto lavorando con JDBC e HSQLDB 2.2.9. Qual è il modo più efficiente ed accurato per inserire una nuova riga in un DB e, successivamente, mantenere il suo valore id (PK set to autoincrement)? La ragione per cui ho bisogno di fare questo è probabilmente abbastanza ovvio, ma io illustrare con un esempio per la discussione:Come recuperare il valore dell'ID PK precedentemente generato automaticamente utilizzando JDBC e HSQLDB

dire che c'è un tavolo Customer che ha un campo PersonId con un vincolo FK riferimento ad una riga da una tabella Person. Voglio creare un nuovo Customer, ma per fare questo ho bisogno di creare prima un nuovo Person e utilizzare il nuovo valore Person.id per impostare Customer.PersonId.

Ho visto quattro modi per affrontare questo:

  1. inserire impostando il campo id al null riga Person. HSQLDB genera automaticamente il prossimo valore id. Quindi eseguire una query sulla tabella Person per ottenere il valore id appena creato e utilizzarlo per creare la nuova riga Customer.

    Questo sembra costoso solo per recuperare un singolo valore intero.

  2. Prendi il id valore successivo nella tabella Person e utilizzarlo nella dichiarazione INSERT per impostare manualmente il valore Person.id. Utilizzare lo stesso valore id per impostare Customer.PersonId. Non è necessaria alcuna lettura successiva dal DB.

    incongruenze potrebbero sorgere se si ottiene un valore id, ma un altro collegamento compie un INSERT nella tabella prima che venga eseguita la mia INSERT INTO Person... dichiarazione.

  3. Eseguire la dichiarazione INSERT, come nell'opzione 1 sopra, impostando id=null per consentire la generazione automatica. Quindi utilizzare il metodo getGeneratedKeys per recuperare le chiavi generate nell'ultima istruzione.

    Ho pensato che fosse una buona opzione, ma non riuscivo a farlo funzionare. Ecco un frammento del mio codice:

    // PreparedStatement prepared previously... 
    preparedStatement.executeUpdate(); 
    ResultSet genKeys = preparedStatement.getGeneratedKeys(); 
    int id; 
    if (genKeys.next()) { 
        id = genKeys.getInt(1); 
    } 
    // Finish up method... 
    

    Questo codice è stato restituisce un vuoto ResultSet per genKeys. Sto usando il metodo getGeneratedKeys in modo errato? Se potessi farlo funzionare, questa potrebbe essere la strada da percorrere.

  4. Anche in questo caso, eseguire la dichiarazione INSERT che consente di generare automaticamente id. Quindi eseguire immediatamente CALL IDENTITY() per recuperare l'ultimo valore id generato dalla connessione (come spiegato here e menzionato nella domanda SO this).

    Anche questa sembra un'opzione ragionevole, anche se devo eseguire un ulteriore executeQuery. Sul lato positivo, mi è stato effettivamente in grado di farlo funzionare con il seguente codice:

    // INSERT statement executed here... 
    statement = connection.createStatement(); 
    ResultSet rs = statement.executeQuery("CALL IDENTITY();"); 
    int id; 
    if (rs.next()) id = rs.getInt(1); 
    // Finish up method... 
    

Così, in sintesi, le prime due opzioni non sono pazzo circa.I secondi due sembrano ok, ma ho potuto solo ottenere l'opzione 4 per funzionare. Quale opzione è preferita e perché? Se l'opzione 3 è la migliore, cosa sto sbagliando? Inoltre, c'è un modo migliore che non ho menzionato? So che parole come "migliori" possono essere soggettive, ma sto lavorando con un semplice DB e voglio la soluzione più diretta che non apre il DB a possibili incoerenze e non aumenta il tasso di errore della transazione (a causa del tentativo per creare un record con uno id già esistente).

Questa sembra una domanda di base (ed essenziale), ma non ho trovato molte indicazioni sul modo migliore per farlo. Grazie.


EDIT: Ho appena trovato this domanda che discute la mia opzione 3. Secondo la risposta accettata, sembra stavo lasciando fuori il parametro Statement.RETURN_GENERATED_KEYS necessario per consentire tale funzionalità. Non ho mostrato il metodo prepareStatement nel mio snippet di codice, ma stavo usando la versione a parametro singolo. Ho bisogno di riprovare usando la versione sovraccaricata, a due parametri.

Ci sono anche alcune altre domande SO che si presentano con quella domanda che sono strettamente correlate alla mia domanda. Quindi, immagino che il mio possa essere considerato un duplicato (non sono sicuro di aver perso prima queste altre domande). Ma mi piacerebbe ancora qualche indicazione su se una soluzione è considerata migliore delle altre. Per ora, se riesco a far funzionare l'opzione 3, probabilmente ci andrò.

+0

Per essere onesti, Io non vedo perché che significa meno bastone alla soluzione No1. Fare una SELECT sull'ID della riga appena inserita ha il solito sovraccarico di una query, ma poi di nuovo, anche tutte le altre opzioni. –

+0

Non sarei 'SELECT'ing su' id', ma piuttosto su una serie di altri campi che comprendono un'altra chiave univoca. Il valore 'id' sarebbe quello che sto cercando. Ma il tuo commento sul "solito sovraccarico di una query" continua a essere valido indipendentemente dai dettagli della query. Tuttavia, non ero sicuro del sovraccarico previsto nell'opzione 3. Ho pensato che le informazioni sarebbero rimaste dall'esecuzione della dichiarazione precedente. Per l'opzione 4 non ero sicuro se l'esecuzione di una query di "CALL IDENTITY()" fosse più economica di una "SELECT" regolare "SELECT". – neizan

+0

Non so se lo è, neanche. Personalmente, non mi preoccuperei di una sola query dopo un inserto, ma naturalmente non conosco le specifiche del tuo compito. –

risposta

1

Non c'è molta azione qui, quindi vado avanti e rispondo per chiudere questa domanda. Dopo aver giocato con le diverse opzioni, e dopo aver visto la domanda this, sono riuscito a far funzionare la mia opzione 3. Come ho accennato nella modifica alla mia domanda, userò l'opzione 3. Anche l'opzione 4 ha funzionato bene, ma poiché la risposta accettata alla domanda collegata è data da una fonte attendibile, mi ci sto attenendo. Vorrei aver visto quella domanda/risposta prima di iniziare questo, avrei risparmiato un po 'di tempo!

+3

L'opzione 3 è la migliore perché la chiave generata viene restituita quando l'istruzione INSERT viene completata, riducendo al minimo le operazioni extra richieste da altre opzioni. – fredt

+0

Questo è quello che ho capito, e il motivo principale per cui sono andato con quell'opzione. Grazie per il feedback. – neizan

+0

Se sei disposto ad usare un ORM, faranno tutto il lavoro per te. Vedi esempio [Colonna Identità Sormula] (http://www.sormula.org/identity/). –

7

io non ho abbastanza fama di commentare la risposta di neizan, ma ecco come ho risolto lo stesso problema:

  • La colonna sembrava una colonna ID, ma non è stato definito come l'identità;
  • Come detto sopra, è necessario specificare RETURN_GENERATED_KEYS.
  • Sembra che se si esegue 2 INSERT in sequenza, il secondo non restituirà i tasti generati. Usa "CALL IDENTITY()" invece.

Esempio con HSQLDB 2.2.9:

CREATE TABLE MY_TABLE (
ID INTEGER IDENTITY, 
NAME VARCHAR(30) 
) 

Poi in Java:

PreparedStatement result = cnx.prepareStatement(
    "INSERT INTO MY_TABLE(ID, NAME) VALUES(NULL, 'TOM');", 
    RETURN_GENERATED_KEYS); 
int updated = result.executeUpdate(); 
if (updated == 1) { 
    ResultSet generatedKeys = result.getGeneratedKeys(); 
    if (generatedKeys.next()) { 
     int key = generatedKeys.getInt(1); 
    } 
} 
Problemi correlati