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()と、状態が「commit done」に設定され、デストラクタは何もしません。それ以外の場合はロールバックします。

例外をチェックするのは悪い考えです。次のことを考慮してください。

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 の場合にのみ「rollback」を呼び出します
于 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 のプライベート boolean メンバーを true に設定できます)。キャッチは、トランザクションが失敗したと仮定して、コードが続行される場所です。

  2. コマンドのいずれかで何かが失敗した瞬間にスローする専用の例外 (DBTransactionRollback と名付けました) を作成する必要があります。したがって、キャッチはトランザクションのロールバックに起因する例外のみをキャッチし、他の例外 (STL など) はキャッチしません。

  3. 例外メカニズムを使用すると、このコードの try/catch ブロックから呼び出される複数の関数にコードを配置できます。ブール値の戻り値やその他のエラー コードの戻り値を処理する必要はありません。

これがあなたの質問に答えることを願っています。

于 2010-04-09T08:31:33.907 に答える
1

私が考えることができる最も簡単な方法は、例外のクラスにプライベートメンバー変数を設定し、それをテストする/デストラクタで適切なアクションを実行することです。

于 2010-04-09T07:11:37.550 に答える