另一個走向極端的錯誤
滿懷信心的新手們可能為自己所掌握的部分知識陶醉不已,剛接觸
資料庫庫事 務處理的准開 發者們也一樣,躊躇滿志地準備將事務機制應用到他的資料處理程式的每一個模組每一條語句中去。的確,事務機制看起來是如此的誘人——簡潔、美妙而又實用, 我當然想用它來避免一切可能出現的錯誤——我甚至想用事務把我的資料操作從頭到尾包裹起來。
看著吧,下面我要從建立一個資料庫開始:
using System; using System.Data; using System.Data.SqlClient; namespace Aspcn { public class DbTran { file://執行交易處理 public void DoTran() { file://建立串連並開啟 SqlConnection myConn=GetConn(); myConn.Open(); SqlCommand myComm=new SqlCommand(); SqlTransaction myTran; myTran=myConn.BeginTransaction(); file://下面綁定串連和事務對象 myComm.Connection=myConn; myComm.Transaction=myTran; file://試圖建立資料庫TestDB myComm.CommandText="CREATE database TestDB"; myComm.ExecuteNonQuery(); file://提交事務 myTran.Commit(); } file:// 擷取資料連線 private SqlConnection GetConn() { string strSql="Data Source=localhost;Integrated Security=SSPI;user id=sa;password="; SqlConnection myConn=new SqlConnection(strSql); return myConn; } } public class Test { public static void Main() { DbTran tranTest=new DbTran(); tranTest.DoTran(); Console.WriteLine("交易處理已經成功完成。"); Console.ReadLine(); } } } //--------------- |
未處理的異常: System.Data.SqlClient.SqlException: 在多語句事務內不允許使用 CREATE DATABASE 語句。
at System.Data.SqlClient.SqlCommand.ExecuteNonQuery() at Aspcn.DbTran.DoTran() at Aspcn.Test.Main() |
注 意,如下的SQL語句不允許出現在事務中:
ALTER DATABASE |
修改資料庫 |
BACKUP LOG |
備份日誌 |
CREATE DATABASE |
建立資料庫 |
DISK INIT |
建立資料庫或交易記錄裝置 |
DROP DATABASE |
刪除資料庫 |
DUMP TRANSACTION |
轉儲交易記錄 |
LOAD DATABASE |
裝載Database Backup複本 |
LOAD TRANSACTION |
裝載交易記錄備份複本 |
RECONFIGURE |
更新使用 sp_configure 系統預存程序更改的配置選項的當前配置(sp_configure 結果集中的 config_value 列)值。 |
RESTORE DATABASE |
還原使用BACKUP命令所作的Database Backup |
RESTORE LOG |
還原使用BACKUP命令所作的記錄備份 |
UPDATE STATISTICS |
在指定的表或索引檢視表中,對一個或多個統計組 (集合)有關索引值分發的資訊進行更新 |
除了這些語句以外,你可以在你的資料庫事務中使用任何合 法的SQL語句。
交易回復
事務的四個特性之一是原子性,其含義是指對於特定操作序列組成的事務,要麼全部完 成,要麼就一件也不做。如果在交易處理的過程中,發生未知的不可預料的錯誤,如何保證事務的原子性呢?當事務中止時,必須執行復原操作,以便消除已經執行 的操作對資料庫的影響。
一般的情況下,在異常處理中使用復原動作是比較好的想法。前面,我們已經得到了一個更新資料庫的程式,並且驗證了它的正確 性,稍微修改一下,可以得到:
//RollBack.cs using System; using System.Data; using System.Data.SqlClient; namespace Aspcn { public class DbTran { file://執行交易處理 public void DoTran() { file://建立串連並開啟 SqlConnection myConn=GetConn(); myConn.Open(); SqlCommand myComm=new SqlCommand(); SqlTransaction myTran; file://建立一個事務 myTran=myConn.BeginTransaction(); file://從此開始,基於該串連的資料操作都被認為是事務的一部分 file://下面綁定串連和事務對象 myComm.Connection=myConn; myComm.Transaction=myTran; try { file:// 定位到pubs資料庫 myComm.CommandText="USE pubs"; myComm.ExecuteNonQuery(); myComm.CommandText="UPDATE roysched SET royalty = royalty * 1.10 WHERE title_id LIKE 'Pc%'"; myComm.ExecuteNonQuery(); file:// 下面使用建立資料庫的語句製造一個錯誤 myComm.CommandText="Create database testdb"; myComm.ExecuteNonQuery(); myComm.CommandText="UPDATE roysched SET royalty = royalty * 1.20 WHERE title_id LIKE 'Ps%'"; myComm.ExecuteNonQuery(); file:// 提交事務 myTran.Commit(); } catch(Exception err) { myTran.Rollback(); Console.Write(" 事務操作出錯,已復原。系統資訊:"+err.Message); } } file://擷取資料連線 private SqlConnection GetConn() { string strSql="Data Source=localhost;Integrated Security=SSPI;user id=sa;password="; SqlConnection myConn=new SqlConnection(strSql); return myConn; } } public class Test { public static void Main() { DbTran tranTest=new DbTran(); tranTest.DoTran(); Console.WriteLine("交易處理已經成功完成。"); Console.ReadLine(); } } } |
首 先,我們在中間人為地製造了一個錯誤——使用前面講過的Create database語句。然後,在異常處理的catch塊中有如下語句:
myTran.Rollback();
當 異常發生時,程式執行流跳轉到catch塊中,首先執行的就是這條語句,它將當前交易回復。在這段程式可以看出,在Create database之前,已經有了一個更新資料庫的操作——將pubs資料庫的roysched表中的所有title_id欄位以“PC”開頭的書籍的 royalty欄位的值都增加0.1倍。但是,由於異常發生而導致的復原使得對於資料庫來說什麼都沒有發生。由此可見,Rollback()方法維護了數 據庫的一致性及事務的原子性。