C#回顧學習筆記三十九:事務
阿新 • • 發佈:2019-02-07
1)事務是什麼?
事務是保證多個操作全部成功時才認為是一次有效操作,當有一個操作失敗時就會認為全部操作無效,並且回到執行操作之前的狀態只有資料改變時(增加、修改、刪除)時才會引發事務,查詢不會引發事務。如果在寫入一個記錄時出現失敗,則事務會讓其他已經寫入的資料回滾,讓資料恢復到修改前的狀態。事務的作用就是:要麼讓任務都執行成功,要麼讓任務都執行失敗。事務的四大特點有:原子性、一致性、隔離性、永續性。
2)為什麼使用事務?
假設有一張使用者賬號餘額表,使用者A向用戶B轉賬100,則資料庫程式碼可以這樣執行:
update GongZiBiao set money=money-100 where name='使用者A'
update GongZiBiao set money=money+100 where name='使用者B'
然而這樣做有個缺點:假設執行了第一條語句後出現什麼異常而導致第二條語句無法執行,則意味著使用者A扣掉了100,而使用者B沒有得到該有的100元。因此引用事務來解決這種問題,有了事務後,若執行一連串資料庫語句時出現錯誤,則前面的語句執行都無效,必須讓所有語句都沒有錯誤,才算真正的執行成功。事務保證了這種重要的資料處理變得合理與安全,對金融和搶票系統來說很重要。
3)如何使用ADO.NET操作事務?
這次的練習就使用SQLserver和VS2013的一個控制檯應用程式來做演示。
第1步,新建控制檯應用程式,在App.config中配置資料庫連線字串。在<configuration>標籤裡配置如下所示的程式碼:
<connectionStrings>
<add name="testConn" connectionString="server=.;database=IsNothing;uid=sa;pwd=145124"/>
</connectionStrings>
其中name屬性的值可以任意設定。server表示資料庫地址,小數點代表就是本地。database表示資料庫名,這裡是IsNothing。uid是資料庫使用者名稱。pwd是資料庫密碼。
第2步,新建資料庫,名字就是IsNothing。新建表UserInfo,表字段設計和表內資料如下圖:
第4步,執行程式。如果程式碼沒有問題,則會出現如下圖所示的情況:
第5步,以上操作似乎還是看不出事務的好處是什麼,因為就算不使用事務,這樣的資料操作似乎也能執行成功。那麼,為了驗證事務的可靠性,先人為地製造一個異常。在如下圖所示的位置新增一句程式碼,這句程式碼絕對會丟擲異常。執行程式,然後觀察看資料庫的值是否改變。如果沒問題,則此時資料庫的值依然跟上面第4步的執行結果一樣。因為在丟擲異常時,事務就讓資料回滾到修改前的狀態了。
第6步,現在把上面程式碼做一下修改,去掉跟事務相關的程式碼然後再執行試一試:
事務是保證多個操作全部成功時才認為是一次有效操作,當有一個操作失敗時就會認為全部操作無效,並且回到執行操作之前的狀態只有資料改變時(增加、修改、刪除)時才會引發事務,查詢不會引發事務。如果在寫入一個記錄時出現失敗,則事務會讓其他已經寫入的資料回滾,讓資料恢復到修改前的狀態。事務的作用就是:要麼讓任務都執行成功,要麼讓任務都執行失敗。事務的四大特點有:原子性、一致性、隔離性、永續性。
2)為什麼使用事務?
假設有一張使用者賬號餘額表,使用者A向用戶B轉賬100,則資料庫程式碼可以這樣執行:
update GongZiBiao set money=money-100 where name='使用者A'
update GongZiBiao set money=money+100 where name='使用者B'
然而這樣做有個缺點:假設執行了第一條語句後出現什麼異常而導致第二條語句無法執行,則意味著使用者A扣掉了100,而使用者B沒有得到該有的100元。因此引用事務來解決這種問題,有了事務後,若執行一連串資料庫語句時出現錯誤,則前面的語句執行都無效,必須讓所有語句都沒有錯誤,才算真正的執行成功。事務保證了這種重要的資料處理變得合理與安全,對金融和搶票系統來說很重要。
3)如何使用ADO.NET操作事務?
這次的練習就使用SQLserver和VS2013的一個控制檯應用程式來做演示。
第1步,新建控制檯應用程式,在App.config中配置資料庫連線字串。在<configuration>標籤裡配置如下所示的程式碼:
<connectionStrings>
<add name="testConn" connectionString="server=.;database=IsNothing;uid=sa;pwd=145124"/>
</connectionStrings>
其中name屬性的值可以任意設定。server表示資料庫地址,小數點代表就是本地。database表示資料庫名,這裡是IsNothing。uid是資料庫使用者名稱。pwd是資料庫密碼。
第2步,新建資料庫,名字就是IsNothing。新建表UserInfo,表字段設計和表內資料如下圖:
解釋:在上面程式碼中,最重要的程式碼是註釋3開始,從這裡宣告事務並使得裡面所有程式碼都屬於事務管理範圍,這樣只要資料操作中途出現異常,則事務讓資料回滾。class Program { static void Main(string[] args) { //1.讀取資料庫連線字串,"testConn"與App.config中的name屬性值保持一致 string connStr = ConfigurationManager.ConnectionStrings["testConn"].ConnectionString; //2.宣告連線資料庫的物件,並開啟資料庫連線 using (SqlConnection conn = new SqlConnection(connStr)) { conn.Open(); using (SqlTransaction tx = conn.BeginTransaction())//3.宣告事務 { using (SqlCommand cmd = new SqlCommand()) { cmd.Connection = conn; cmd.Transaction = tx; try { cmd.CommandText = "update UserInfo set Age=Age-1 where Id=1"; int row1 = cmd.ExecuteNonQuery(); cmd.CommandText = "update UserInfo set Age=Age+1 where Id=2"; int row2 = cmd.ExecuteNonQuery(); tx.Commit();//注意這裡,如果上面兩句sql語句都執行成功,這裡才完成提交。 Console.WriteLine("執行語句1影響的行數是{0}", row1); Console.WriteLine("執行語句2影響的行數是{0}", row2); } catch (Exception ex) { tx.Rollback();//try內部程式碼發生異常,就執行回滾讓資料回到原始狀態 Console.WriteLine("發生異常,異常資訊:", ex.ToString()); } } } Console.ReadKey(); } }
第4步,執行程式。如果程式碼沒有問題,則會出現如下圖所示的情況:
第5步,以上操作似乎還是看不出事務的好處是什麼,因為就算不使用事務,這樣的資料操作似乎也能執行成功。那麼,為了驗證事務的可靠性,先人為地製造一個異常。在如下圖所示的位置新增一句程式碼,這句程式碼絕對會丟擲異常。執行程式,然後觀察看資料庫的值是否改變。如果沒問題,則此時資料庫的值依然跟上面第4步的執行結果一樣。因為在丟擲異常時,事務就讓資料回滾到修改前的狀態了。
第6步,現在把上面程式碼做一下修改,去掉跟事務相關的程式碼然後再執行試一試:
class Program
{
static void Main(string[] args)
{
string connStr = ConfigurationManager.ConnectionStrings["testConn"].ConnectionString;
using (SqlConnection conn = new SqlConnection(connStr))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand())
{
cmd.Connection = conn;
try
{
cmd.CommandText = "update UserInfo set Age=Age-1 where Id=1";
int row1 = cmd.ExecuteNonQuery();
int exp = Convert.ToInt32("abc");//注意這裡,故意製造一個異常
cmd.CommandText = "update UserInfo set Age=Age+1 where Id=2";
int row2 = cmd.ExecuteNonQuery();
Console.WriteLine("執行語句1影響的行數是{0}", row1);
Console.WriteLine("執行語句2影響的行數是{0}", row2);
}
catch (Exception ex)
{
Console.WriteLine("發生異常,異常資訊:", ex.ToString());
}
}
}
Console.ReadKey();
}
}
上面的程式碼會在執行成功第一個sql語句後丟擲異常,這樣就造成第二個sql語句無法執行。所以觀察資料庫可以發現,第一個使用者的年齡減了1歲,而第二個使用者的年齡依然沒變。這就是沒有事務時的壞處,如果是金融轉賬,沒有事務控制的話就會造成虧損。
4)如何使用SQLserver處理事務
SQLserver裡自帶的語句也可以完成事務處理,就拿上面的練習來說,在SQLserver裡可以這麼做:
begin try
begin transaction--設定反悔點,開啟事務(出錯時可以回到這裡),可以簡寫為tran
delete from T_Persons where Id=8
delete from T_Classes
commit transaction--提交事務,不反悔(沒問題,直接走的意思)
end try
begin catch
rollback transaction--回滾事務,如果出錯就回到反悔點(反悔了要回去起點的意思)
end catch