2014-05-20 12 views
5

Ho un blocco di codice che aggiorna i record in un database. Esempio ridotto:Posso riutilizzare un PreparedStatement se riscontra un'eccezione transitoria?

... 
statement = connection.prepareStatement(
    "INSERT INTO thistable (name) VALUES (?)", 
    PreparedStatement.RETURN_GENERATED_KEYS 
); 
statement.setString(1, "Fred"); 
statement.addBatch(); 
statement.setString(2, "Joe"); 
statement.addBatch(); 
statement.executeBatch(); 
... 

Questo è parte di un codice, che sta elaborando un sacco di record, e il codice è in esecuzione un sacco di fili per la velocità. Va tutto bene, ma man mano che il carico aumenta sull'ambiente live, ho notato che sono stati lanciati alcuni SQLTransientException s. Sembra che non posso fare nulla su questi, tranne ritentare la transazione.

La mia domanda è: il lotto potrebbe essere stato cancellato anche se l'istruzione non è riuscita? Devo semplicemente riprovare semplicemente la riga executeBatch o devo ricreare l'intero batch? Ed è lo stesso per le dichiarazioni non batch?


In breve, e più in generale, questo è un buon modo per gestire le eccezioni transitorie?

statement.setString(1, "Fred"); 
statement.addBatch(); 
statement.setString(2, "Joe"); 
statement.addBatch(); 
for(int attempt = 0; ; attempt ++) { 
    try { 
     statement.executeBatch(); 
     break; 
    } catch(SQLTransientException ex) { 
     if(attempt >= 3) { 
      throw ex; 
     } 
     try { 
      Thread.sleep(attempt * attempt * 100); 
     } catch(InterruptedException ex2) { 
      throw ex; 
     } 
    } 
} 
+0

Ho taggato questa domanda MySQL perché è quello che stiamo attualmente utilizzando, ma sto cercando di capire il comportamento standard, piuttosto che il comportamento specifico dell'implementazione, dal momento che stiamo migrando presto a Postgres. – Dave

+0

In teoria 'SQLTransientException' deve essere lanciato solo se l'errore consente operazioni che possono essere riprovate senza" senza alcun intervento da parte delle funzionalità a livello di applicazione ". –

+0

Avete uno stato SQL per questa eccezione (getSQLState())? – Mani

risposta

2

Poiché il API non fornisce molte informazioni sul comportamento quando succede SQLTransientException parte sotto

when the driver has determined that the timeout value that was specified by the 
setQueryTimeout method has been exceeded and has at least attempted 
to cancel the currently running Statement 

verificato l'attuazione Mysql JDBC4.

Nella mia osservazione, ho potuto vedere che c'è un possibile SQLTimeoutException (vale a dire SQLTransientException) può accadere se il tempo scaduto.

E mentre scaduta, non è solo schiarendo le ultime dichiarazioni, si sta chiarendo tutta la partita (almeno in MYSQL Attuazione)

// we timeout the entire batch, not individual statements 

modo che una parte è chiaro.

E come per JDBC specifica

The statement’s batch is reset to empty once executeBatch returns 

Indipendentemente executeBatch lancia un'eccezione/avere successo sarebbe cancellare il batch.

particolare Mysql Prepared Statement cancella il lotto al blocco Finaly a executeBatch() metodo

finally { 
     this.statementExecuting.set(false); 
     clearBatch(); 
     } 

Quindi la logica non funziona, dal momento che non sta aggiungendo le dichiarazioni in lotti di nuovo.

Quindi aggiungere nuovamente le istruzioni a batch e rieseguire. è molto probabile che tu possa finire di nuovo in Timeout. Quindi prima trova il timeout. se possibile, scrivi alcune informazioni su causa/stato nei registri di esecuzione.

+0

Cancellare manualmente (per ogni evenienza) e ricopiarlo è quindi. È un peccato - speravo che questo potesse essere nascosto in un metodo. – Dave

+0

Solo per aggiungere (per i futuri visitatori) che per la relativa domanda relativa alle istruzioni non batch, i valori non devono essere ripopolati, secondo questa documentazione: http://docs.oracle.com/javase/7/ docs/api/java/sql/PreparedStatement.html # clearParameters() – Dave

0

qual è il messaggio di eccezione? Non sono sicuro se non è riuscito a causa di un problema di connessione mysql. Inoltre, non penso sia opportuno elaborare una lunga richiesta. Potresti eseguirlo più volte eseguendo un pezzo alla volta per raggiungere il tuo obiettivo.

+0

Ho risposto a questo nei commenti sulla domanda. Per riferimento futuro, non inviare commenti/domande come risposte. – Dave

Problemi correlati