2010-04-09 30 views
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 
} 

但我怎麼會寫它的析構函數?

回答

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

+1

+1這就是這樣的事情。但有一個問題:如果忘記調用commit()會怎麼樣? – sharptooth 2010-04-09 07:47:20

+2

如果你忘記創建事務變量會怎麼樣?你無法防止所有的錯誤。 – 2010-04-09 08:12:36

+2

@sharptooth:如果您忘記了首先想要做出的變更,該怎麼辦?我不認爲你可以做什麼來防止無能。 – jalf 2010-04-09 08:50:54

1

我能想到的最簡單的方法是在異常中的類中設置私有成員變量,然後在析構函數中測試它/執行適當的操作。

3

您可能會使用以下邏輯:

  1. 添加初始化爲您的交易類commit_done布爾值。
  2. 在你的構造函數中,「開始」事務。
  3. 添加一種方法來「提交」交易並相應地更新commit_done
  4. 在你的析構函數,稱爲 「回退」,只在commit_done仍然
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. 有一個在「捕捉」需要什麼:析構函數將假定自動除非成功調用commit()方法(例如,可以將DBTransaction的某個私有布爾成員設置爲true),否則回滾。如果事務失敗,那麼代碼將繼續執行。

  2. 您應該創建一個專用異常(我將其命名爲DBTransactionRollback),以便在某個命令中發生某些失敗。因此,捕獲只會捕獲事務回滾驅動的異常,而不是其他異常(如STL等)

  3. 使用異常機制使您可以將代碼放在多個函數中,從此try /捕獲代碼塊,而不必處理布爾返回和其他錯誤代碼返回。

希望這回答你的問題。

相關問題