本文將涉及到在.net框架下使用c#語言操縱數(shù)據(jù)庫事務的各個方面。
體驗sql語言的事務機制
作為大型的企業(yè)級數(shù)據(jù)庫,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自帶的示例數(shù)據(jù)庫pubs,提交事務后,將為所有暢銷計算機書籍支付的版稅增加 10%。
打開sql server2000的查詢分析器,選擇pubs數(shù)據(jù)庫,然后運行這段程序,結(jié)果顯而易見。
可是如何在c#程序中運行呢?我們記得在普通的sql查詢中,一般需要把查詢語句賦值給salcommand.commandtext屬性,這里也就像普通的sql查詢語句一樣,將這些語句賦給sqlcommand.commandtext屬性即可。要注意的一點是,其中的"go"語句標志著sql批處理的結(jié)束,編寫sql腳本是需要的,但是在這里是不必要的。我們可以編寫如下的程序來驗證這個想法:
//transql.csusing system;
using system.data;
using system.data.sqlclient;
namespace aspcn
{
public class dbtransql
{
file://將事務放到sql server中執(zhí)行
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("事務操作出錯,系統(tǒng)信息:"+err.message);
}
finally
{
myconn.close();
}
}
file://獲取數(shù)據(jù)連接
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("事務處理已經(jīng)成功完成。");
console.readline();
}
}
}
注意到其中的sqlcommand對象mycomm,它的commandtext屬性僅僅是前面sql代碼字符串連接起來即可,當然,其中的"go"語句已經(jīng)全部去掉了。這個語句就像普通的查詢一樣,程序?qū)ql文本事實上提交給dbms去處理了,然后接收返回的結(jié)果(如果有結(jié)果返回的話)。
很自然,我們最后看到了輸出"事務處理已經(jīng)成功完成",再用企業(yè)管理器查看pubs數(shù)據(jù)庫的roysched表,所有title_id字段以"pc"開頭的書籍的royalty字段的值都增加了0.1倍。
這里,我們并沒有使用ado.net的事務處理機制,而是簡單地將執(zhí)行事務的sql語句當作普通的查詢來執(zhí)行,因此,事實上該事務完全沒有用到.net的相關特性。
了解.net中的事務機制
如你所知,在.net框架中主要有兩個命名空間(namespace)用于應用程序同數(shù)據(jù)庫系統(tǒng)的交互:system.data.sqlclient和system.data.oledb。前者專門用于連接microsoft公司自己的sql server數(shù)據(jù)庫,而后者可以適應多種不同的數(shù)據(jù)庫。這兩個命名空間中都包含有專門用于管理數(shù)據(jù)庫事務的類,分別是system.data.sqlclient.sqltranscation類和system.data.oledb.oledbtranscation類。
就像它們的名字一樣,這兩個類大部分功能是一樣的,二者之間的主要差別在于它們的連接機制,前者提供一組直接調(diào)用 sql server 的對象,而后者使用本機 ole db 啟用數(shù)據(jù)訪問。 事實上,ado.net 事務完全在數(shù)據(jù)庫的內(nèi)部處理,且不受 microsoft 分布式事務處理協(xié)調(diào)器 (dtc) 或任何其他事務性機制的支持。本文將主要介紹system.data.sqlclient.sqltranscation類,下面的段落中,除了特別注明,都將使用system.data.sqlclient.sqltranscation類。
事務的開啟和提交
現(xiàn)在我們對事務的概念和原理都了然于心了,并且作為已經(jīng)有一些基礎的c#開發(fā)者,我們已經(jīng)熟知編寫數(shù)據(jù)庫交互程序的一些要點,即使用sqlconnection類的對象的open()方法建立與數(shù)據(jù)庫服務器的連接,然后將該連接賦給sqlcommand對象的connection屬性,將欲執(zhí)行的sql語句賦給它的commandtext屬性,于是就可以通過sqlcommand對象進行數(shù)據(jù)庫操作了。對于我們將要編寫的事務處理程序,當然還需要定義一個sqltransaction類型的對象。并且看到sqlcommand對象的transcation屬性,我們很容易想到新建的sqltransaction對象應該與它關聯(lián)起來。
基于以上認識,下面我們就開始動手寫我們的第一個事務處理程序。我們可以很熟練地寫出下面這一段程序:
//dotran.csusing system;
using system.data;
using system.data.sqlclient;
namespace aspcn
{
public class dbtran
{
file://執(zhí)行事務處理
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數(shù)據(jù)庫
mycomm.commandtext="use pubs";
mycomm.executenonquery();
file://更新數(shù)據(jù)
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("事務操作出錯,系統(tǒng)信息:"+err.message);
}
finally
{
myconn.close();
}
}
file://獲取數(shù)據(jù)連接
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("事務處理已經(jīng)成功完成。");
console.readline();
}
}
}
顯然,這個程序非常簡單,我們非常自信地編譯它,但是,出乎意料的結(jié)果使我們的成就感頓時煙消云散:
error cs1501: 重載"sqltransaction"方法未獲取"0"參數(shù)
是什么原因呢?注意到我們初始化的代碼:
sqltransaction mytran=new sqltransaction();
顯然,問題出在這里,事實上,sqltransaction類并沒有公共的構(gòu)造函數(shù),我們不能這樣新建一個sqltrancaction類型的變量。在事務處理之前確實需要有一個sqltransaction類型的變量,將該變量關聯(lián)到sqlcommand類的transcation屬性也是必要的,但是初始化方法卻比較特別一點。在初始化sqltransaction類時,你需要使用sqlconnection類的begintranscation()方法:
sqltransaction mytran; mytran=myconn.begintransaction();
該方法返回一個sqltransaction類型的變量。在調(diào)用begintransaction()方法以后,所有基于該數(shù)據(jù)連接對象的sql語句執(zhí)行動作都將被認為是事務mytran的一部分。同時,你也可以在該方法的參數(shù)中指定事務隔離級別和事務名稱,如:
sqltransaction mytran;
mytran=myconn.begintransaction(isolationlevel.readcommitted,"sampletransaction");
關于隔離級別的概念我們將在隨后的內(nèi)容中探討,在這里我們只需牢記一個事務是如何被啟動,并且關聯(lián)到特定的數(shù)據(jù)鏈接的。
先不要急著去搞懂我們的事務都干了些什么,看到這一行:
mytran.commit();
是的,這就是事務的提交方式。該語句執(zhí)行后,事務的所有數(shù)據(jù)庫操作將生效,并且為數(shù)據(jù)庫事務的持久性機制所保持--即使系統(tǒng)在這以后發(fā)生致命錯誤,該事務對數(shù)據(jù)庫的影響也不會消失。
對上面的程序做了修改之后我們可以得到如下代碼(為了節(jié)約篇幅,重復之處已省略,請參照前文):
//dotran.cs……}
file://執(zhí)行事務處理
public void dotran()
{
file://建立連接并打開
sqlconnection myconn=getconn();
myconn.open();
sqlcommand mycomm=new sqlcommand();
file://sqltransaction mytran=new sqltransaction();
file://注意,sqltransaction類無公開的構(gòu)造函數(shù)
sqltransaction mytran;
file://創(chuàng)建一個事務
mytran=myconn.begintransaction();
try
{
file://從此開始,基于該連接的數(shù)據(jù)操作都被認為是事務的一部分
file://下面綁定連接和事務對象
mycomm.connection=myconn;
mycomm.transaction=mytran; file://定位到pubs數(shù)據(jù)庫
mycomm.commandtext="use pubs";
mycomm.executenonquery();//更新數(shù)據(jù)
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("事務操作出錯,系統(tǒng)信息:"+err.message);
}
finally
{
myconn.close();
}
}
……
到此為止,我們僅僅掌握了如何開始和提交事務。下一步我們必須考慮的是在事務中可以干什么和不可以干什么。
|
新聞熱點
疑難解答
圖片精選