2012年2月9日 星期四

C# : 使用 MySQL 的 Transaction

資料庫程式的運作上,我們常常會需要查出某筆資料取得某項資訊,然後依照此資訊修改另一筆資料,修改完再次取出資料經過一些程式判斷的結果,又再對某個資料表新增或刪除資料.... 一連串複雜的動作需要一次完成,通常這是一瞬間就能處理完成的事,所以不應該會中斷;但是當伺服器相當忙錄時,同時有多位使用者對資料庫要求這一系列的連續動作,那麼動作之間就很可能不是處理同一使用者的請求,取得的資料也可能不是預計的正確資料,所以新增、修改、刪除也可能會出錯,為了使這些工作能夠正確,我們可以使用 Transaction 來確保工作過程無誤,如果工作不順利而發生問題無法正確完成,則退回到工作開始前的狀態。

在 MySQL 使用 Transaction 需要特別注意的一點是資料表的儲存引擎必須設置為 InnoDB 才有支援,如果是 MyISAM 則即使程式撰寫時有使用 Transaction,但實際運作並不會有 Transaction 的功用。

以下範例,在處理 MySQL 語句之前開始 Transaction,如果過程中發生不符合預期的情況,則丟出例外並使整個交易復原到未工作前的狀態..



//開啟連線
MySqlConnection conn = new MySqlConnection("server=127.0.0.1;user=root;database=test;port=3306;password=1111;");
conn.Open();

//開始 Transaction
MySqlTransaction transaction = conn.BeginTransaction();

try{

//使用 Transaction 查資料
MySqlCommand cmd = new MySqlCommand("SELECT * FROM test_table WHERE id=1" , conn , transaction);
MySqlDataReader reader = cmd.ExecuteReader();
while(reader.Read()){
...
}
reader.Close();

//使用 Transaction 修改資料
cmd = new MySqlCommand("UPDATE test_table SET name='newName' WHERE id=1" , conn , transaction);
cmd.ExecuteNonQuery();

//交易完成
transaction.Commit();

// MySQL 處理發生問題,補捉到 Connector/Net 丟出的例外
}catch(MySqlException ex){

//取消交易,復原至交易前
transaction.Rollback();

//列出訊息
Console.WriteLine(ex.Message);

//補捉到其它例外
}catch(Exception ex){

//取消交易,復原至交易前
transaction.Rollback();

//列出訊息
Console.WriteLine(ex.Message);
}

以上先補捉 MySQL 處理發生問題所丟出的例外,這時候如果不復原回交易前,那麼相關資料必定會出現問題;還有,就是程式執行期丟出的例外可能造成程式停止運作等問題,所以即使在 MySQL 處理上沒有問題,但因為程式停止運作,所以之後與資料庫相關的動作也將中斷,為了避免動作不完全,應該也要復原到此項工作開始之前;另外,在整個交易過程,也許在某個動作所獲得的回應與我們預期可行的值相違背,也就是在我們自定義的程式設計邏輯上認定為錯誤回應時,我們可能也會丟出某些例外及訊息,這時也可視情況考慮是否為這個例外而取消交易。