什麼是數據庫事務
數據庫事務是指做爲單個邏輯工做單元執行的一系列操做。
設想網上購物的一次交易,其付款過程至少包括如下幾步數據庫操做:
· 更新客戶所購商品的庫存信息
· 保存客戶付款信息--可能包括與銀行系統的交互
· 生成訂單而且保存到數據庫中
· 更新用戶相關信息,例如購物數量等等
正常的狀況下,這些操做將順利進行,最終交易成功,與交易相關的全部數據庫信息也成功地更新。可是,若是在這一系列過程當中任何一個環節出了差錯,例如在更新商品庫存信息時發生異常、該顧客銀行賬戶存款不足等,都將致使交易失敗。一旦交易失敗,數據庫中全部信息都必須保持交易前的狀態不變,好比最後一步更新用戶信息時失敗而致使交易失敗,那麼必須保證這筆失敗的交易不影響數據庫的狀態--庫存信息沒有被更新、用戶也沒有付款,訂單也沒有生成。不然,數據庫的信息將會一片混亂而不可預測。
數據庫事務正是用來保證這種狀況下交易的平穩性和可預測性的技術。
數據庫事務的ACID屬性
事務處理能夠確保除非事務性單元內的全部操做都成功完成,不然不會永久更新面向數據的資源。經過將一組相關操做組合爲一個要麼所有成功要麼所有失敗的單元,能夠簡化錯誤恢復並使應用程序更加可靠。一個邏輯工做單元要成爲事務,必須知足所謂的ACID(原子性、一致性、隔離性和持久性)屬性:
· 原子性
事務必須是原子工做單元;對於其數據修改,要麼全都執行,要麼全都不執行。一般,與某個事務關聯的操做具備共同的目標,而且是相互依賴的。若是系統只執行這些操做的一個子集,則可能會破壞事務的整體目標。原子性消除了系統處理操做子集的可能性。
· 一致性
事務在完成時,必須使全部的數據都保持一致狀態。在相關數據庫中,全部規則都必須應用於事務的修改,以保持全部數據的完整性。事務結束時,全部的內部數據結構(如 B 樹索引或雙向鏈表)都必須是正確的。某些維護一致性的責任由應用程序開發人員承擔,他們必須確保應用程序已強制全部已知的完整性約束。例如,當開發用於轉賬的應用程序時,應避免在轉賬過程當中任意移動小數點。
· 隔離性
由併發事務所做的修改必須與任何其它併發事務所做的修改隔離。事務查看數據時數據所處的狀態,要麼是另外一併發事務修改它以前的狀態,要麼是另外一事務修改它以後的狀態,事務不會查看中間狀態的數據。這稱爲可串行性,由於它可以從新裝載起始數據,而且重播一系列事務,以使數據結束時的狀態與原始事務執行的狀態相同。當事務可序列化時將得到最高的隔離級別。在此級別上,從一組可並行執行的事務得到的結果與經過連續運行每一個事務所得到的結果相同。因爲高度隔離會限制可並行執行的事務數,因此一些應用程序下降隔離級別以換取更大的吞吐量。
· 持久性
事務完成以後,它對於系統的影響是永久性的。該修改即便出現致命的系統故障也將一直保持。
DBMS的責任和咱們的任務
企業級的數據庫管理系統(DBMS)都有責任提供一種保證事務的物理完整性的機制。就經常使用的SQL Server2000系統而言,它具有鎖定設備隔離事務、記錄設備保證事務持久性等機制。所以,咱們沒必要關心數據庫事務的物理完整性,而應該關注在什麼狀況下使用數據庫事務、事務對性能的影響,如何使用事務等等。
本文將涉及到在.net框架下使用C#語言操縱數據庫事務的各個方面。
體驗SQL語言的事務機制
做爲大型的企業級數據庫,SQL Server2000對事務提供了很好的支持。咱們可使用SQL語句來定義、提交以及回滾一個事務。
以下所示的SQL代碼定義了一個事務,而且命名爲"MyTransaction"(限於篇幅,本文並不討論如何編寫SQL語言程序,請讀者自行參考相關書籍):html
DECLARE @TranName VARCHAR(20) SELECT @TranName = 'MyTransaction' BEGIN TRANSACTION @TranNameGOUSE pubs GO UPDATE roysched SET royalty = royalty * 1.10 WHERE title_id LIKE 'Pc%' GO COMMIT TRANSACTION MyTransaction GO
這裏用到了SQL Server2000自帶的示例數據庫pubs,提交事務後,將爲全部暢銷計算機書籍支付的版稅增長 10%。
打開SQL Server2000的查詢分析器,選擇pubs數據庫,而後運行這段程序,結果顯而易見。
但是如何在C#程序中運行呢?咱們記得在普通的SQL查詢中,通常須要把查詢語句賦值給SalCommand.CommandText屬性,這裏也就像普通的SQL查詢語句同樣,將這些語句賦給SqlCommand.CommandText屬性便可。要注意的一點是,其中的"GO"語句標誌着SQL批處理的結束,編寫SQL腳本是須要的,可是在這裏是沒必要要的。咱們能夠編寫以下的程序來驗證這個想法:數據庫
//TranSql.csusing System; using System.Data; using System.Data.SqlClient; namespace Aspcn { public class DbTranSql { file://將事務放到SQL Server中執行 public void DoTran() { file://創建鏈接並打開 SqlConnection myConn=GetConn();myConn.Open(); SqlCommand myComm=new SqlCommand(); try { myComm.Connection=myConn; myComm.CommandText="DECLARE @TranName VARCHAR(20) "; myComm.CommandText+="SELECT @TranName = 'MyTransaction' "; myComm.CommandText+="BEGIN TRANSACTION @TranName "; myComm.CommandText+="USE pubs "; myComm.CommandText+="UPDATE roysched SET royalty = royalty * 1.10 WHERE title_id LIKE 'Pc%' "; myComm.CommandText+="COMMIT TRANSACTION MyTransaction "; myComm.ExecuteNonQuery(); } catch(Exception err) { throw new ApplicationException("事務操做出錯,系統信息:"+err.Message); } finally { myConn.Close(); } } 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() { DbTranSql tranTest=new DbTranSql(); tranTest.DoTran(); Console.WriteLine("事務處理已經成功完成。"); Console.ReadLine(); } } }
注意到其中的SqlCommand對象myComm,它的CommandText屬性僅僅是前面SQL代碼字符串鏈接起來便可,固然,其中的"GO"語句已經所有去掉了。這個語句就像普通的查詢同樣,程序將SQL文本事實上提交給DBMS去處理了,而後接收返回的結果(若是有結果返回的話)。
很天然,咱們最後看到了輸出"事務處理已經成功完成",再用企業管理器查看pubs數據庫的roysched表,全部title_id字段以"PC"開頭的書籍的royalty字段的值都增長了0.1倍。
這裏,咱們並無使用ADO.net的事務處理機制,而是簡單地將執行事務的SQL語句看成普通的查詢來執行,所以,事實上該事務徹底沒有用到.net的相關特性。
瞭解.net中的事務機制
如你所知,在.net框架中主要有兩個命名空間(namespace)用於應用程序同數據庫系統的交互:System.Data.SqlClient和System.Data.OleDb。前者專門用於鏈接Microsoft公司本身的SQL Server數據庫,然後者能夠適應多種不一樣的數據庫。這兩個命名空間中都包含有專門用於管理數據庫事務的類,分別是System.Data.SqlClient.SqlTranscation類和System.Data.OleDb.OleDbTranscation類。
就像它們的名字同樣,這兩個類大部分功能是同樣的,兩者之間的主要差異在於它們的鏈接機制,前者提供一組直接調用 SQL Server 的對象,然後者使用本機 OLE DB 啓用數據訪問。 事實上,ADO.net 事務徹底在數據庫的內部處理,且不受 Microsoft 分佈式事務處理協調器 (DTC) 或任何其餘事務性機制的支持。本文將主要介紹System.Data.SqlClient.SqlTranscation類,下面的段落中,除了特別註明,都將使用System.Data.SqlClient.SqlTranscation類。
事務的開啓和提交
如今咱們對事務的概念和原理都瞭然於心了,而且做爲已經有一些基礎的C#開發者,咱們已經熟知編寫數據庫交互程序的一些要點,即便用SqlConnection類的對象的Open()方法創建與數據庫服務器的鏈接,而後將該鏈接賦給SqlCommand對象的Connection屬性,將欲執行的SQL語句賦給它的CommandText屬性,因而就能夠經過SqlCommand對象進行數據庫操做了。對於咱們將要編寫的事務處理程序,固然還須要定義一個SqlTransaction類型的對象。而且看到SqlCommand對象的Transcation屬性,咱們很容易想到新建的SqlTransaction對象應該與它關聯起來。
基於以上認識,下面咱們就開始動手寫咱們的第一個事務處理程序。咱們能夠很熟練地寫出下面這一段程序:
編程
//DoTran.csusing 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=new SqlTransaction(); try { myComm.Connection=myConn; myComm.Transaction=myTran; file://定位到pubs數據庫 myComm.CommandText="USE pubs"; myComm.ExecuteNonQuery(); file://更新數據 file://將全部的計算機類圖書 myComm.CommandText="UPDATE roysched SET royalty = royalty * 1.10 WHERE title_id LIKE 'Pc%'"; myComm.ExecuteNonQuery();//提交事務 myTran.Commit(); } catch(Exception err) { throw new ApplicationException("事務操做出錯,系統信息:"+err.Message); } finally { myConn.Close(); } } 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(); } } }
顯然,這個程序很是簡單,咱們很是自信地編譯它,可是,出乎意料的結果使咱們的成就感頓時煙消雲散:
error CS1501: 重載"SqlTransaction"方法未獲取"0"參數
是什麼緣由呢?注意到咱們初始化的代碼:安全
SqlTransaction myTran=new SqlTransaction();
顯然,問題出在這裏,事實上,SqlTransaction類並無公共的構造函數,咱們不能這樣新建一個SqlTrancaction類型的變量。在事務處理以前確實須要有一個SqlTransaction類型的變量,將該變量關聯到SqlCommand類的Transcation屬性也是必要的,可是初始化方法卻比較特別一點。在初始化SqlTransaction類時,你須要使用SqlConnection類的BeginTranscation()方法:服務器
SqlTransaction myTran; myTran=myConn.BeginTransaction();
該方法返回一個SqlTransaction類型的變量。在調用BeginTransaction()方法之後,全部基於該數據鏈接對象的SQL語句執行動做都將被認爲是事務MyTran的一部分。同時,你也能夠在該方法的參數中指定事務隔離級別和事務名稱,如:數據結構
SqlTransaction myTran; myTran=myConn.BeginTransaction(IsolationLevel.ReadCommitted,"SampleTransaction");
關於隔離級別的概念咱們將在隨後的內容中探討,在這裏咱們只需牢記一個事務是如何被啓動,而且關聯到特定的數據連接的。
先不要急着去搞懂咱們的事務都幹了些什麼,看到這一行:併發
myTran.Commit();
是的,這就是事務的提交方式。該語句執行後,事務的全部數據庫操做將生效,而且爲數據庫事務的持久性機制所保持--即便系統在這之後發生致命錯誤,該事務對數據庫的影響也不會消失。
對上面的程序作了修改以後咱們能夠獲得以下代碼(爲了節約篇幅,重複之處已省略,請參照前文):框架
//DoTran.cs……} file://執行事務處理 public void DoTran() { file://創建鏈接並打開 SqlConnection myConn=GetConn(); myConn.Open(); SqlCommand myComm=new SqlCommand(); file://SqlTransaction myTran=new SqlTransaction(); file://注意,SqlTransaction類無公開的構造函數 SqlTransaction myTran; file://建立一個事務 myTran=myConn.BeginTransaction(); try { file://今後開始,基於該鏈接的數據操做都被認爲是事務的一部分 file://下面綁定鏈接和事務對象 myComm.Connection=myConn; myComm.Transaction=myTran; file://定位到pubs數據庫 myComm.CommandText="USE pubs"; myComm.ExecuteNonQuery();//更新數據 file://將全部的計算機類圖書 myComm.CommandText="UPDATE roysched SET royalty = royalty * 1.10 WHERE title_id LIKE 'Pc%'"; myComm.ExecuteNonQuery(); file://提交事務 myTran.Commit(); } catch(Exception err) { throw new ApplicationException("事務操做出錯,系統信息:"+err.Message); } finally { myConn.Close(); } } //……
到此爲止,咱們僅僅掌握瞭如何開始和提交事務。下一步咱們必須考慮的是在事務中能夠幹什麼和不能夠幹什麼。分佈式
另外一個走向極端的錯誤
滿懷信心的新手們可能爲本身所掌握的部分知識陶醉不已,剛接觸數據庫庫事務處理的準開發者們也同樣,躊躇滿志地準備將事務機制應用到他的數據處理程序的每個模塊每一條語句中去。的確,事務機制看起來是如此的誘人——簡潔、美妙而又實用,我固然想用它來避免一切可能出現的錯誤——我甚至想用事務把個人數據操做從頭至尾包裹起來。
看着吧,下面我要從建立一個數據庫開始:函數
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 | 裝載數據庫備份複本 |
LOAD TRANSACTION | 裝載事務日誌備份複本 |
RECONFIGURE | 更新使用 sp_configure 系統存儲過程更改的配置選項的當前配置(sp_configure 結果集中的 config_value 列)值。 |
RESTORE 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()方法維護了數據庫的一致性及事務的原子性。
使用存儲點
事務只是一種最壞狀況下的保障措施,事實上,平時系統的運行可靠性都是至關高的,錯誤不多發生,所以,在每次事務執行以前都檢查其有效性顯得代價過高——絕大多數的狀況下這種耗時的檢查是沒必要要的。咱們不得不想另一種辦法來提升效率。
事務存儲點提供了一種機制,用於回滾部分事務。所以,咱們能夠沒必要在更新以前檢查更新的有效性,而是預設一個存儲點,在更新以後,若是沒有出現錯誤,就繼續執行,不然回滾到更新以前的存儲點。存儲點的做用就在於此。要注意的是,更新和回滾代價很大,只有在遇到錯誤的可能性很小,並且預先檢查更新的有效性的代價相對很高的狀況下,使用存儲點纔會很是有效。
使用.net框架編程時,你能夠很是簡單地定義事務存儲點和回滾到特定的存儲點。下面的語句定義了一個存儲點「NoUpdate」:
myTran.Save("NoUpdate");
當你在程序中建立同名的存儲點時,新建立的存儲點將替代原有的存儲點。
在回滾事務時,只需使用Rollback()方法的一個重載函數便可:
myTran.Rollback("NoUpdate");
下面這段程序說明了回滾到存儲點的方法和時機:
隔離級別的概念
企業級的數據庫每一秒鐘均可能應付成千上萬的併發訪問,於是帶來了併發控制的問題。由數據庫理論可知,因爲併發訪問,在不可預料的時刻可能引起以下幾個能夠預料的問題:
髒讀:包含未提交數據的讀取。例如,事務1 更改了某行。事務2 在事務1 提交更改以前讀取已更改的行。若是事務1 回滾更改,則事務2 便讀取了邏輯上從未存在過的行。
不可重複讀取:當某個事務不止一次讀取同一行,而且一個單獨的事務在兩次(或屢次)讀取之間修改該行時,由於在同一個事務內的屢次讀取之間修改了該行,因此每次讀取都生成不一樣值,從而引起不一致問題。
幻象:經過一個任務,在之前由另外一個還沒有提交其事務的任務讀取的行的範圍中插入新行或刪除現有行。帶有未提交事務的任務因爲該範圍中行數的更改而沒法重複其原始讀取。
如你所想,這些狀況發生的根本緣由都是由於在併發訪問的時候,沒有一個機制避免交叉存取所形成的。而隔離級別的設置,正是爲了不這些狀況的發生。事務準備接受不一致數據的級別稱爲隔離級別。隔離級別是一個事務必須與其它事務進行隔離的程度。較低的隔離級別能夠增長併發,但代價是下降數據的正確性。相反,較高的隔離級別能夠確保數據的正確性,但可能對併發產生負面影響。
根據隔離級別的不一樣,DBMS爲並行訪問提供不一樣的互斥保證。在SQL Server數據庫中,提供四種隔離級別:未提交讀、提交讀、可重複讀、可串行讀。這四種隔離級別能夠不一樣程度地保證併發的數據完整性:
隔離級別 | 髒 讀 | 不可重複讀取 | 幻 像 |
未提交讀 | 是 | 是 | 是 |
提交讀 | 否 | 是 | 是 |
可重複讀 | 否 | 否 | 是 |
可串行讀 | 否 | 否 | 否 |
能夠看出,「可串行讀」提供了最高級別的隔離,這時併發事務的執行結果將與串行執行的徹底一致。如前所述,最高級別的隔離也就意味着最低程度的併發,所以,在此隔離級別下,數據庫的服務效率事實上是比較低的。儘管可串行性對於事務確保數據庫中的數據在全部時間內的正確性至關重要,然而許多事務並不老是要求徹底的隔離。例如,多個做者工做於同一本書的不一樣章節。新章節能夠在任意時候提交到項目中。可是,對於已經編輯過的章節,沒有編輯人員的批准,做者不能對此章節進行任何更改。這樣,儘管有未編輯的新章節,但編輯人員仍能夠確保在任意時間該書籍項目的正確性。編輯人員能夠查看之前編輯的章節以及最近提交的章節。這樣,其它的幾種隔離級別也有其存在的意義。
在.net框架中,事務的隔離級別是由枚舉System.Data.IsolationLevel所定義的:
[Flags] [Serializable] public enum IsolationLevel
其成員及相應的含義以下:
成 員 | 含 義 |
Chaos | 沒法改寫隔離級別更高的事務中的掛起的更改。 |
ReadCommitted | 在正在讀取數據時保持共享鎖,以免髒讀,可是在事務結束以前能夠更改數據,從而致使不可重複的讀取或幻像數據。 |
ReadUncommitted | 能夠進行髒讀,意思是說,不發佈共享鎖,也不接受獨佔鎖。 |
RepeatableRead | 在查詢中使用的全部數據上放置鎖,以防止其餘用戶更新這些數據。防止不可重複的讀取,可是仍能夠有幻像行。 |
Serializable | 在DataSet上放置範圍鎖,以防止在事務完成以前由其餘用戶更新行或向數據集中插入行。 |
Unspecified | 正在使用與指定隔離級別不一樣的隔離級別,可是沒法肯定該級別。 |
顯而意見,數據庫的四個隔離級別在這裏都有映射。
默認的狀況下,SQL Server使用ReadCommitted(提交讀)隔離級別。
關於隔離級別的最後一點就是若是你在事務執行的過程當中改變了隔離級別,那麼後面的命名都在最新的隔離級別下執行——隔離級別的改變是當即生效的。有了這一點,你能夠在你的事務中更靈活地使用隔離級別從而達到更高的效率和併發安全性。
最後的忠告
無疑,引入事務處理是應對可能出現的數據錯誤的好方法,可是也應該看到事務處理須要付出的巨大代價——用於存儲點、回滾和併發控制所須要的CPU時間和存儲空間。
本文的內容只是針對Microsoft SQL Server數據庫的,對應於.net框架中的System.Data.SqlClient命名空間,對於使用OleDb的情形,具體的實現稍有不一樣,但這不是本文的內容,有興趣的讀者能夠到.net中華網(www.aspcn.com)的論壇裏找到答案。
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 { myComm.CommandText="use pubs"; myComm.ExecuteNonQuery(); myTran.Save("NoUpdate"); myComm.CommandText="UPDATE roysched SET royalty = royalty * 1.10 WHERE title_id LIKE 'Pc%'"; myComm.ExecuteNonQuery(); file://提交事務 myTran.Commit(); } catch(Exception err) { file://更新錯誤,回滾到指定存儲點 myTran.Rollback("NoUpdate"); throw new ApplicationException("事務操做出錯,系統信息:"+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(); } } }
很明顯,在這個程序中,更新無效的概率是很是小的,並且在更新前驗證其有效性的代價至關高,所以咱們無須在更新以前驗證其有效性,而是結合事務的存儲點機制,提供了數據完整性的保證。
原文地址:http://www.cnblogs.com/freeliver54/archive/2008/07/29/1255415.html