隨筆-348  評論-598  文章-0  trackbacks-0

            數據庫事務是指作為單個邏輯工作單元執行的一系列操作。

          設想網上購物的一次交易,其付款過程至少包括以下幾步數據庫操作:

            · 更新客戶所購商品的庫存信息

            · 保存客戶付款信息--可能包括與銀行系統的交互

            · 生成訂單并且保存到數據庫中

            · 更新用戶相關信息,例如購物數量等等

          正常的情況下,這些操作將順利進行,最終交易成功,與交易相關的所有數據庫信息也成功地更新。但是,如果在這一系列過程中任何一個環節出了差錯,例如在更新商品庫存信息時發生異常、該顧客銀行帳戶存款不足等,都將導致交易失敗。一旦交易失敗,數據庫中所有信息都必須保持交易前的狀態不變,比如最后一步更新用戶信息時失敗而導致交易失敗,那么必須保證這筆失敗的交易不影響數據庫的狀態--庫存信息沒有被更新、用戶也沒有付款,訂單也沒有生成。否則,數據庫的信息將會一片混亂而不可預測。

          數據庫事務正是用來保證這種情況下交易的平穩性和可預測性的技術。

            數據庫事務的ACID屬性

          事務處理可以確保除非事務性單元內的所有操作都成功完成,否則不會永久更新面向數據的資源。通過將一組相關操作組合為一個要么全部成功要么全部失敗的單元,可以簡化錯誤恢復并使應用程序更加可靠。一個邏輯工作單元要成為事務,必須滿足所謂的ACID(原子性、一致性、隔離性和持久性)屬性:

            · 原子性

          事務必須是原子工作單元;對于其數據修改,要么全都執行,要么全都不執行。通常,與某個事務關聯的操作具有共同的目標,并且是相互依賴的。如果系統只執行這些操作的一個子集,則可能會破壞事務的總體目標。原子性消除了系統處理操作子集的可能性。

            · 一致性

          事務在完成時,必須使所有的數據都保持一致狀態。在相關數據庫中,所有規則都必須應用于事務的修改,以保持所有數據的完整性。事務結束時,所有的內部數據結構(如 B 樹索引或雙向鏈表)都必須是正確的。某些維護一致性的責任由應用程序開發人員承擔,他們必須確保應用程序已強制所有已知的完整性約束。例如,當開發用于轉帳的應用程序時,應避免在轉帳過程中任意移動小數點。

            · 隔離性

          由并發事務所作的修改必須與任何其它并發事務所作的修改隔離。事務查看數據時數據所處的狀態,要么是另一并發事務修改它之前的狀態,要么是另一事務修改它之后的狀態,事務不會查看中間狀態的數據。這稱為可串行性,因為它能夠重新裝載起始數據,并且重播一系列事務,以使數據結束時的狀態與原始事務執行的狀態相同。當事務可序列化時將獲得最高的隔離級別。在此級別上,從一組可并行執行的事務獲得的結果與通過連續運行每個事務所獲得的結果相同。由于高度隔離會限制可并行執行的事務數,所以一些應用程序降低隔離級別以換取更大的吞吐量。

            · 持久性

           事務完成之后,它對于系統的影響是永久性的。該修改即使出現致命的系統故障也將一直保持。
          DBMS的責任和我們的任務

          企業級的數據庫管理系統(DBMS)都有責任提供一種保證事務的物理完整性的機制。就常用的SQL Server2000系統而言,它具備鎖定設備隔離事務、記錄設備保證事務持久性等機制。因此,我們不必關心數據庫事務的物理完整性,而應該關注在什么情況下使用數據庫事務、事務對性能的影響,如何使用事務等等。

          本文將涉及到在.net框架下使用C#語言操縱數據庫事務的各個方面。

          +++@@@+++

          體驗SQL語言的事務機制

          作為大型的企業級數據庫,SQL Server2000對事務提供了很好的支持。我們可以使用SQL語句來定義、提交以及回滾一個事務。

          如下所示的SQL代碼定義了一個事務,并且命名為"MyTransaction"(限于篇幅,本文并不討論如何編寫SQL語言程序,請讀者自行參考相關書籍):

          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();
            }
          }
          ……

          到此為止,我們僅僅掌握了如何開始和提交事務。下一步我們必須考慮的是在事務中可以干什么和不可以干什么。
           

          該文章轉載自'大智の博客':http://www.csafe.cn/article.asp?id=271



          ---------------------------------------------------------
          專注移動開發

          Android, Windows Mobile, iPhone, J2ME, BlackBerry, Symbian
          posted on 2007-05-10 22:43 TiGERTiAN 閱讀(4495) 評論(2)  編輯  收藏 所屬分類: Other technique

          評論:
          # re: 什么是數據庫事務 2008-05-21 11:42 | 鳳兒
          非常好,支持!  回復  更多評論
            
          # re: 什么是數據庫事務 2008-05-21 11:46 | TiGERTiAN
          @鳳兒
          呵呵,轉的。。  回復  更多評論
            
          主站蜘蛛池模板: 余姚市| 虎林市| 故城县| 和平县| 柞水县| 宜宾市| 长葛市| 科技| 柳州市| 榆树市| 枣阳市| 攀枝花市| 海阳市| 荥阳市| 金阳县| 广元市| 锦州市| 玉山县| 商丘市| 济源市| 昌邑市| 友谊县| 安达市| 余姚市| 英超| 永安市| 瑞昌市| 丰城市| 兴和县| 塔城市| 桂阳县| 清苑县| 荣昌县| 象山县| 洛扎县| 繁峙县| 兴义市| 抚宁县| 资阳市| 乌兰察布市| 新竹县|