I created a class to encapsulate database transaction to make sure it is either rolled back or committed when an exception is thrown. The problem is that both creating and closing a transaction may fail. Since the transaction is being closed in the destructor, how can I handle a failure without throwing an exception? Obviously if the TransactionLock
object is being destroyed as a result of an exception being thrown somewhere down the stack, this will result in program termination.
// RAII class for database transaction
class TransactionLock {
public:
TransactionLock(QSqlDatabase& db) :
m_db(db),
m_query(db),
m_committed(false)
{
bool ok = m_query.exec("BEGIN IMMEDIATE TRANSACTION");
if (!ok)
{
throw IOException(m_query.lastError());
}
}
~TransactionLock()
{
bool ok = m_committed ? m_db.commit() : m_db.rollback();
// if (!ok) throw?
}
void commitTransaction()
{
m_committed = true;
}
private:
QSqlDatabase& m_db;
QSqlQuery m_query;
bool m_committed;
};
You should never throw in the destructor. Not only because destructor can be called as a result of exception stack unwinding, but also because it is logically senseless.
You can not prevent the object from being destructed, so exception gives you nothing at all. In your particular case, you need to redesign your class. The standard approach is usually to do automatic rollback in the destructor. Commit transaction is an explicit operation with error code.