在編程中,經常需要使用事務。所謂事務,就是一系列必須都成功的操作,只要有一步操作失敗,所有其他的步驟也必須撤銷。比如用ASP開發一個網路硬碟系統,其使用者註冊部分要做的事有:
1、將使用者資訊記入資料庫
2、為使用者開個檔案夾用於儲存
3、初始化使用者動作記錄
這三步必須使用事務,否則萬一磁碟操作失敗,而沒有撤銷資料庫操作,就會造成只能登陸而不能操作的“死使用者”現象。
由於資料庫系統特殊的發展曆史,小至Access,大到DB2,無不帶有事務支援。因此上述步驟可以如下表示:
以下是引用片段:
On Error Resume Next
第一步:
在事務環境下把使用者資訊記入資料庫
If Err Then
關閉串連
退出
Else
第二步:建立檔案夾
If Err Then
復原第一步資料庫操作,退出
Else
第三步:在事務環境下動作記錄資料庫
If Err Then
復原第一步操作,刪除第二步建立的檔案夾
退出
End If
End If
End If
提交第一步資料庫操作的事務
提交第二步資料庫操作的事務
End
每一步都需要進行判斷,如果失敗,還需要手工復原前面多步操作,使程式變得複雜、難懂。如果今後更新了程式,增加其他步驟,還需要嵌套更多層的If...Else...End If,使程式流程更加複雜。
正確的解決辦法是使用ASP的事務控制功能。IIS通過和MTS服務聯絡,可以控制多種支援事務的系統,當程式發出“失敗”的訊號時,所有支援事務的系統均將自動復原,即使操作已經正式完成;對不支援事務的操作也提供了方便的手工復原方式。上面的例子用ASP事務控制功能重寫如下:
複製代碼 代碼如下:
<%@ TRANSACTION = Required %>
On Error Resume Next
Set Conn=Server.CreateObject("ADODB.Connection")
Conn.Open ....
Conn.Execute "INSERT...."
Conn.Close
Set Conn=Nothing
Set Conn2=Server.CreateObject("ADODB.Connection")
Conn2.Open ....
Conn2.Execute "INSERT...."
Conn2.Close
Set Conn2=Nothing
Set FSO=Server.CreateObject("Scripting.FilesystemObject")
FSO.CreateFolder "...."
If Err Then
ObjectContext.SetAbort '通知所有支援事務的組件復原,並運行手工復原代碼
Else
ObjectContext.SetComplete
End If
Set FSO=Nothing
Sub OnTransactionAbort
Response.Write "錯誤"
FSO.DeleteFile Server.Mappath("a.txt") 'FSO的手工復原——刪除檔案夾
End Sub
Sub OnTransactionCommit
Response.Write "勝利完成任務"
End Sub
%>
第一行的<%@ TRANSACTION = Required %>表示這一頁ASP檔案需要MTS的事務支援。中間的各個操作都按普通順序書寫,而不用考慮復原問題。在程式最後判斷是否有錯誤。如果有,調用ObjectContext的SetAbort方法,IIS會通過MTS服務通知所有支援事務的組件復原(主要是資料庫),並且運行Sub OnTransactionAbort對不支援事務的操作手工復原;如果沒有發生錯誤,調用ObjectContext的SetComplete方法,則會運行Sub OnTransactionCommit來顯示成功的訊息。
整個ASP程式不需要為判斷錯誤和復原操作書寫多餘的代碼,只須在最後進行判斷,即使今後增加了多步操作,也只需要在Sub OnTransactionAbort中進行控制即可,非常方便,程式員可以專註於過程編寫而不是書寫錯誤修正代碼。
其實ASP還提供了許多更有用的功能,等著我們使用,千萬不要以為ASP使用指令碼語言,功能就一定弱。
以下是引用片段:
<%
'asp交易處理。
'測試資料庫為sql server,伺服器為本機,資料庫名為test,表名為a,兩個欄位id(int)主鍵標識,num(int)
set conn=server.CreateObject("adodb.connection")
strConn="provider=sqloledb.1;persist security info=false;uid=sa;pwd=sa;Initial Catalog=test;Data Source=."
conn.Open strConn
'以上代碼建立資料庫連接
conn.BeginTrans '事務開始
strSql1="update a set num=1000 where id=24" '第一個sql語句為update。(文法正確)
strSql2="insert into a(num) values('a')" '第二個sql語句為錯誤的sql語句
strSql3="insert into a(num) values(33333)" '第三個sql語句為正確的sql語句
call conn.execute(strSql1)
call conn.execute(strSql2)
call conn.execute(strSql3)
if conn.Errors.Count=0 then
conn.CommitTrans '如果沒有conn錯誤,則執行事務提交
else
conn.RollbackTrans '否則復原
end if
%>
以上代碼經調試,可以正常的進行交易處理。但是有時候,我們並不想將編譯錯誤顯示給使用者。
則我們需要在conn.BeginTrans後面加上On error resume next
但是因為用到了On error resume next。conn.Errors.Count只能獲得最後一個資料庫操作的conn返回的結果 。上面的三個sql語句,因為最後一個sql語句是正確的,則此交易處理就無效了。那我們需要對出錯處理作出相對應的修改。
if conn.Errors.Count=0 then應該改為if err.number=0 then
這樣,我們可以在資料庫復原後同時做出其他相對應的操作或者提示。修改後的代碼如下:
以下是引用片段:
<%
set conn=server.CreateObject("adodb.connection")
strConn="provider=sqloledb.1;persist security info=false;uid=sa;pwd=sa;Initial Catalog=test;Data Source=."
conn.Open strConn
'以上代碼建立資料庫連接
conn.BeginTrans '事務開始
on error resume next '增加的代碼
strSql1="update a set num=1000 where id=24" '第一個sql語句為update。(文法正確)
strSql2="insert into a(num) values('a')" '第二個sql語句為錯誤的sql語句
strSql3="insert into a(num) values(33333)" '第三個sql語句為正確的sql語句
call conn.execute(strSql1)
call conn.execute(strSql2)
call conn.execute(strSql3)
if err.number =0 then
conn.CommitTrans '如果沒有conn錯誤,則執行事務提交
else
conn.RollbackTrans '否則復原
'復原後的其他動作
strerr=err.Description
Response.Write "資料庫錯誤!錯誤記錄檔:<font color=red>"&strerr &"</font>"
Response.End
end if
%>