2013-04-02 11 views
27

Ho un problema con try-with-resources e sto chiedendo solo per essere sicuro. Posso usarlo, se devo reagire all'eccezione, e ho ancora bisogno della risorsa nel blocco catch? Esempio dato è questo:Rollback transazioni su SQLException utilizzando il nuovo blocco try-with-resources

try (java.sql.Connection con = createConnection()) 
{ 
    con.setAutoCommit(false); 
    Statement stm = con.createStatement(); 
    stm.execute(someQuery); // causes SQLException 
} 
catch(SQLException ex) 
{ 
    con.rollback(); 
    // do other stuff 
} 

temo che sto ancora condannato a usare il vecchio try-catch-finally, in questo caso, anche in base alla documentazione Oracle - "blocchi catch e infine in un try-con-risorse dichiarazione, ogni blocco catch o finally viene eseguito dopo che le risorse dichiarate sono state chiuse. "

+2

In questo caso se la connessione stessa non ha funzionato, non è necessario ripristinarla. Lo scopo di 'con' è limitato a provare solo il blocco. – learningloop

+0

Questa domanda può essere d'aiuto. http://stackoverflow.com/questions/9260159/java-7-automatic-resource-management-jdbc –

+0

Di tutte le opzioni interessanti fornite, continuo a preferire la soluzione originale "try-catch-finally' – Adam

risposta

33

In base alle specifiche della lingua, la connessione verrà chiusa prima dell'esecuzione della clausola di cattura (http://docs.oracle.com/javase/specs/jls/se7/html/jls-14.html#jls-14.20.3.2).

Una possibile soluzione è quella di nidificare istruzioni try-con-risorse:

try (java.sql.Connection con = createConnection()) 
{ 
    con.setAutoCommit(false); 
    try (Statement stm = con.createStatement()) 
    { 
     stm.execute(someQuery); // causes SQLException 
    } 
    catch(SQLException ex) 
    { 
     con.rollback(); 
     con.setAutoCommit(true); 
     throw ex; 
    } 
    con.commit(); 
    con.setAutoCommit(true); 
} 

Speriamo che illustra il punto. Questo dovrebbe essere migliorato un po 'se si pensa di usarlo nel codice di produzione.

Ad esempio, se si utilizza un pool di connessioni, è necessario restituire la connessione così come è stata ottenuta, quindi con.setAutoCommit (true); dovrebbe essere fatto in una clausola finale. Ciò significherebbe che il tentativo esterno con le risorse dovrebbe essere un tradizionale tentativo di cattura-finalmente.

+0

, thx –

+1

Be consapevole che chiamare ['setAutoCommit'] (http://docs.oracle.com/javase/8/docs/api/java/sql/Connection.html#setAutoCommit-boolean-) chiama un COMMIT su qualsiasi transazione in sospeso. Quindi fai attenzione nel codice più complicato. –

+1

"Ciò significherebbe che la prova esterna con le risorse dovrebbe essere un tradizionale tentativo di cattura-finalmente." - Quindi, il modello "prova con risorse" è completamente inutile per le connessioni JDBC con i pool di connessioni ...? – Itai

3

Nell'esempio sopra, penso che sia meglio inserire con.commit() all'interno di nidificazione try-catch perché può anche generare SQLException.

try (java.sql.Connection con = createConnection()) 
    { 
     con.setAutoCommit(false); 
     try (Statement stm = con.createStatement()) 
     { 
      stm.execute(someQuery); // causes SQLException 
      con.commit();   // also causes SQLException! 
     } 
     catch(SQLException ex) 
     { 
      con.rollback(); 
      throw ex; 
     }finally{ 
      con.setAutoCommit(true); 
     } 
    } 

Abbiamo avuto un tale problema nel nostro ambiente di produzione con sessioni non chiuse.

+2

È necessario utilizzare un'istruzione finally per impostare l'autocommit tornando alla verità Questo ti impedirebbe di avere questa affermazione due volte – AxelH

6
//try with resources 
    try(Connection conn = this.connectionProvider.getConnection()){//auto close BEFORE reach this , catch block, so we need a inner try block for statement 
     boolean oldAutoCommit=conn.getAutoCommit(); 
     conn.setAutoCommit(false);//auto commit to false 
     try(
      Statement stm = con.createStatement() 
     ){ 
      stm.execute(someQuery); // causes SQLException 
      conn.commit();//commit 
     } 
     catch (SQLException ex){ 
      conn.rollback();//error, rollback 
      throw ex;//If you need to throw the exception to the caller 
     } 
     finally { 
      conn.setAutoCommit(oldAutoCommit);//reset auto commit 
     } 
    } 
10

Nel codice si sta rilevando "SQLException" per eseguire il reset autoCommit. Qualsiasi tipo di eccezione di runtime (ad esempio un'eccezione di puntatore nullo) si bolla dal codice senza reimpostare il commit automatico.

La sintassi try-with-resource fa sì che il compilatore generi un codice meraviglioso per coprire tutti i percorsi di esecuzione e per tenere il passo con tutte le eccezioni soppresse attraverso le chiusure. Con un paio di classi di supporto è possibile inserire commit/rollback e ripristinare-autocommit nel processo di generazione del codice:

import java.sql.SQLException; 
import java.sql.Connection; 

public class AutoRollback implements AutoCloseable { 

    private Connection conn; 
    private boolean committed; 

    public AutoRollback(Connection conn) throws SQLException { 
     this.conn = conn;   
    } 

    public void commit() throws SQLException { 
     conn.commit(); 
     committed = true; 
    } 

    @Override 
    public void close() throws SQLException { 
     if(!committed) { 
      conn.rollback(); 
     } 
    } 

} 

public class AutoSetAutoCommit implements AutoCloseable { 

    private Connection conn; 
    private boolean originalAutoCommit; 

    public AutoSetAutoCommit(Connection conn, boolean autoCommit) throws SQLException { 
     this.conn = conn; 
     originalAutoCommit = conn.getAutoCommit(); 
     conn.setAutoCommit(autoCommit); 
    } 

    @Override 
    public void close() throws SQLException { 
     conn.setAutoCommit(originalAutoCommit); 
    } 

} 

Ora è possibile controllare il rollback e autocommit con la "prova con risorsa" la sintassi del genere:

try(Connection conn = getConnection(), 
     AutoSetAutoCommit a = new AutoSetAutoCommit(conn,false), 
     AutoRollback tm = new AutoRollback(conn)) 
    { 

     // Do stuff 

     tm.commit(); 
    } 
+1

Codice interessante, non ho avuto il tempo di provare l'interfaccia AutoCloseable.Questo sembra essere un buon approche per sostituire la clausola finally.In produzione il codice sarebbe molto più semplice con un solo tentativo-con-risorsa.) – AxelH

+0

1. Perché non dichiari "getta SQ LException "in close() metodi? Perché ti avvolgi solo come un'eccezione? 2. Perché non crei tutte le risorse all'interno di un singolo blocco try? AFAIK sarebbero stati chiusi in un ordine opposto, tu li hai dichiarati. – Sabine

+0

Ottimi punti! Cambierò le chiusure per lanciare Eccezioni SQL e combinarle in un'unica soluzione come suggerito da te e AxelH. Grazie per il feedback. – ChrisCantrell

Problemi correlati