2010-04-09 11 views
6

ho qualche codice per aggiornare una tabella di database che assomigliaAvere un distruttore intraprendere azioni diverse a seconda che si è verificata un'eccezione

try 
{ 
    db.execute("BEGIN"); 
    // Lots of DELETE and INSERT  
    db.execute("COMMIT"); 
} 
catch (DBException&) 
{ 
    db.execute("ROLLBACK"); 
} 

vorrei avvolgere la logica dell'operazione in una classe RAII così ho potuto basta scrivere

{ 
    DBTransaction trans(db); 
    // Lots of DELETE and INSERT 
} 

ma come scriverei il distruttore per questo?

risposta

12

Uso seguente:

transaction tr(db); 
... 
tr.commit(); 

Quando tr.commit() completa imposta lo stato a "impegnarsi fatto" e il distruttore non fa nulla, altrimenti rollback.

Controllando per eccezione è cattiva idea, prendere in considerazione:

transaction tr(db); 
... 
if(something_wrong) 
    return; // Not throw 
... 
tr.commit(); 

In questo caso probabilmente si aspettano piuttosto rollback poi commettere ma commit sarebbe stato fatto.

Edit: ma se si vuole ancora male, date un'occhiata su std::uncaught_exception() ma leggere questa prima http://www.gotw.ca/gotw/047.htm

+1

+1 Ecco come sono fatte queste cose. C'è un problema però: cosa succede se ti dimentichi di chiamare commit()? – sharptooth

+2

E se dimenticassi di creare una variabile di transazione? Non puoi prevenire tutti gli errori. –

+2

@sharptooth: cosa succede se hai dimenticato di apportare le modifiche che avresti voluto commettere in primo luogo? Non penso che ci sia molto che puoi fare per proteggere dall'incompetenza. – jalf

1

Il modo più semplice a cui pensare è impostare una variabile membro privata nella classe nell'eccezione e testarla/eseguire l'azione appropriata nel distruttore.

3

si potrebbe utilizzare la seguente logica:

  1. Aggiungi un commit_done valore booleano inizializzato a falso alla classe Transaction.
  2. Nel costruttore, "inizia" la transazione.
  3. Aggiungere un metodo per "confermare" la transazione e aggiornare commit_done di conseguenza.
  4. Nel vostro distruttore, chiamata "rollback" solo se commit_done è ancora falsa
2

Rimuovendo la gestione delle eccezioni, si sta paralizzando la vostra Raii.

Il codice dovrebbe essere

try 
{ 
    DBTransaction trans(db) ; 

    // Lots of DELETE and INSERT 
    // should one fail, a DBTransactionRollback exception will be thrown 

    trans.commit() ; 
} 
catch(const DBTransactionRollback & e) 
{ 
    // If really needed, you could extract failure information from "e" 
} 

Le differenze con il tuo codice originale sono ciò che ha motivato la mia risposta:

  1. Non c'è nulla di necessario nel "catturare": Il distruttore assumerà un automatico rollback a meno che il metodo commit() sia stato chiamato con successo ( che potrebbe, ad esempio, impostare un membro booleano privato di DBTransaction su true). Il problema è dove il codice continuerà, supponendo che la transazione fallisca.

  2. Dovresti creare un'eccezione dedicata (l'ho chiamata DBTransactionRollback) per lanciare il momento in cui qualcosa non riesce in uno dei tuoi comandi. In questo modo, il catch rileverà solo l'eccezione motivata dal rollback della transazione e non altre eccezioni (come STL, ecc.)

  3. L'utilizzo del meccanismo di eccezione consente di inserire il codice in più funzioni, chiamate da questo try/cattura il blocco del codice, senza dover gestire i ritorni booleani e altri ritorni del codice di errore.

Spero che questo risponda alla tua domanda.

Problemi correlati