c++ exception transactions raii

c++ - Tener un destructor toma diferentes acciones dependiendo de si se produjo una excepción



exception transactions (4)

Tengo un código para actualizar una tabla de base de datos que se parece a

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

Me gustaría ajustar la lógica de transacción en una clase de RAII para poder escribir

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

pero ¿cómo escribiría el destructor para eso?


Al eliminar el manejo de excepciones, está paralizando su RAII.

El código debe ser

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" }

Las diferencias con tu código original son las que motivaron mi respuesta:

  1. No se necesita nada en la "captura": el destructor asumirá una reversión automática a menos que se invoque el método commit () con éxito (lo que podría, por ejemplo, establecer algún miembro booleano privado de DBTransaction en verdadero ). La captura es donde el código continuará, asumiendo que la transacción falló.

  2. Debes crear una excepción dedicada (la llamé DBTransactionRollback) para lanzar el momento en que algo falla en uno de tus comandos. Por lo tanto, la captura solo detectará la excepción motivada por la reversión de transacción, y no otras excepciones (como STL, etc.)

  3. El uso del mecanismo de excepción le permite poner su código en múltiples funciones, llamadas desde este bloque try / catch de código, sin tener que lidiar con retornos booleanos y otros retornos de código de error.

Espero que esto responda a su pregunta.


La manera más fácil que puedo pensar sería establecer una variable de miembro privada en la clase en la excepción, y probarla / realizar la acción apropiada en el destructor.


Puede usar la siguiente lógica:

  1. Agregue un valor booleano commit_done inicializado a falso a su clase Transaction.
  2. En tu constructor, "comienza" la transacción.
  3. Agregue un método para "confirmar" la transacción y actualice commit_done en consecuencia.
  4. En su destructor, llame a "rollback" solo si commit_done sigue siendo falso

Use lo siguiente:

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

Cuando tr.commit() completa, establece el estado en "commit done" y destructor no hace nada, de lo contrario, se revierte.

Verificar la excepción es una mala idea, considere:

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

En este caso, probablemente esperas que se restituya y luego se comprometa, pero se completará el compromiso.

Editar: pero si aún lo desea, eche un vistazo a std::uncaught_exception() pero lea esto primero http://www.gotw.ca/gotw/047.htm