6

我有一些代码来更新看起来像的数据库表

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

我想将事务逻辑包装在 RAII 类中,这样我就可以编写

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

但是我将如何为它编写析构函数呢?

4

4 回答 4

12

使用以下:

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

完成tr.commit()后,它将状态设置为“提交完成”并且析构函数不执行任何操作,否则它会回滚。

检查异常是个坏主意,请考虑:

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

在这种情况下,您可能希望回滚而不是提交,但提交会完成。

编辑:但是如果您仍然非常想要它,请看一下std::uncaught_exception()但先阅读http://www.gotw.ca/gotw/047.htm

于 2010-04-09T07:26:24.550 回答
3

您可以使用以下逻辑:

  1. 将初始化为false的commit_done布尔值添加到您的 Transaction 类。
  2. 在您的构造函数中,“开始”事务。
  3. 添加一个方法来“提交”事务并相应地更新commit_done
  4. 在您的析构函数中,仅当commit_done仍然为false时才调用“回滚”
于 2010-04-09T07:13:11.483 回答
2

通过删除异常处理,您正在削弱您的 RAII。

代码应该是

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

与您的原始代码的差异是促使我回答的原因:

  1. “catch”中不需要任何东西:析构函数将假定自动回滚,除非成功调用了 commit() 方法(例如,可以将 DBTransaction 的一些私有布尔成员设置为 true)。假设事务失败,catch 是代码将继续的地方。

  2. 您应该创建一个专用的异常(我将其命名为 DBTransactionRollback),以便在您的一个命令中出现故障时抛出异常。因此,catch 只会捕获事务回滚引发的异常,而不会捕获其他异常(如 STL 等)

  3. 异常机制的使用使您可以将代码放在多个函数中,从这个 try/catch 代码块调用,而无需处理布尔返回和其他错误代码返回。

希望这能回答你的问题。

于 2010-04-09T08:31:33.907 回答
1

我能想到的最简单的方法是在异常中的类中设置一个私有成员变量,并在析构函数中对其进行测试/执行适当的操作。

于 2010-04-09T07:11:37.550 回答