VB+ADO檢測資料庫並行作業和處理並解決並發衝突本文章係為轉載,無作者出處
粘上摘自網路的地址 http://www.java-asp.net/vb/200602/t_17625.html
VB+ADO檢測資料庫並行作業和處理並解決並發衝突
前言:
資料庫並發問題詳述http://www.csdn.net/Develop/read_article.asp?id=24366已經說明了並發的嚴重性與危害性。下面講述VB+ADO來處理並行作業的實際案例:
在以前DAO中可以對資料庫進行記錄鎖,頁面鎖,表鎖來處理並行作業,還可以使用交易處理,那麼現在怎麼用ADO來檢測並處理資料庫的並行作業呢?
相關背景知識:
ADO中對資料庫的也是採用鎖定的方法來實現的,還可以用事務來做。事務有個特點就是:要麼全成功,要麼就全失敗。那麼在實際工作中有可能只有幾條或一小部分的記錄有衝突,只要對那一小部分的記錄進行處理就行了。ADO也使用來鎖定來實現。
那麼什麼是鎖定?
鎖定是一種進程,DBMS 通過該進程限制多使用者環境中對行的訪問。當一行或一列被獨佔鎖定時,不允許其他使用者在釋放鎖定之前訪問鎖定的資料。這確保了兩個使用者無法同時更新一行中的同一列。
從資源角度而言,鎖定的成本可能非常高昂,只有在需要保持資料完整性的情況下才應當使用此功能。在每秒有數百或數千使用者試圖訪問某個記錄的資料庫(例如串連到 Internet 的資料庫)中,不必要的鎖定將很快導致應用程式效能的下降。
可選擇適當的鎖定選項來控制資料來源和 ADO 遊標庫管理並發性的方式。
開啟 Recordset 之前先設定 LockType 屬性,以指定提供者開啟它時使用的鎖定類型。讀取該屬性以返回開啟的 Recordset 對象中使用的鎖定類型。
提供者可能不支援所有鎖定類型。如果提供者不支援要求的 LockType 設定,則替換為另一種鎖定類型。若要確定 Recordset 對象中實際可用的鎖定功能,將soppurts 方法與 adUpdate 和 adUpdateBatch 一起使用。
如果 CursorLocation 屬性設定為 adUseClient,則不支援 adLockPessimistic 設定。如果設定了不支援的值,將不產生錯誤,而使用所支援的最相近的 LockType。
LockType 屬性在 Recordset 關閉時為讀/寫,而在 Recordset 開啟時為唯讀。
鎖定類型種類(LockType 屬性):
1.adLockBatchOptimistic
指示開放式批更新。需要批更新模式。
許多應用程式都一次提取多行,然後需要進行相應的更新,這些更新包括所有需要插入、更新或刪除的行的完整集合。使用批遊標只需一次往返伺服器,因而導致更
新效能的提高和網路通訊量的降低。使用批遊標庫可建立靜態資料指標,然後斷開到資料來源的串連。這時就可以對行變更,然後重新串連到資料來源並將更改以批的形
式發布到資料來源。
2.adLockOptimistic
提供者使用開放式鎖定,僅在調用 Update 方法時鎖定記錄。這意味著另一個使用者有可能會利用您編輯記錄和調用 Update 的時間間隔更改資料,這就會引起衝突。而使用此鎖定類型時發生衝突的幾率很低,即使發生了衝突也會很快得到解決。
3.adLockPessimistic
指示逐個記錄保守式鎖定。提供者要確保記錄編輯成功,通常在編輯之前立即在資料來源鎖定記錄。當然,這意味著一旦您開始編輯記錄,這些記錄就會對其他使用者不
可用,知道您通過調用 Update 釋放鎖定。如果您的系統不提供對資料的並發更改,例如預定系統,那麼可使用此鎖定類型。
4.adLockReadOnly
指示唯讀記錄。無法改變資料。唯讀鎖定是速度“最快”的鎖定類型,因為它不要求伺服器保持對記錄的鎖定。衝突
5.adLockUnspecified
未指定鎖定類型。
檢測和解決衝突:
如果在立即模式中處理
Recordset,則很少出現並發問題。另一方面,如果應用程式使用批模式更新,則在儲存由一個正在編輯記錄的使用者所作的更改之前,編輯同一個記錄的另
一個使用者比較有可能會更改記錄。在這種情況下,需要應用程式準確處理衝突。無論是哪種情況,都可以使用 ADO 提供的 Field 對象的
UnderlyingValue 和 OriginalValue 屬性來處理這些類型的衝突。將這些屬性與 Recordset 的 Resync
方法和 Filter 屬性配合使用。
檢測錯誤
在批更新期間 ADO 遇到衝突時,將在 Errors 集合中放入警告。因此,調用 BatchUpdate
之後,一定要立即檢查是否有錯誤,如果找到了錯誤,則應當假設已遇到衝突並開始進行測試。第一步要將 Recordset 的 Filter
屬性設定為等於 adFilterConflictingRecords。該設定將使
Recordset對象限制為只顯示那些發生衝突的記錄。如果這一步之後 RecordCount
屬性等於零,就說明錯誤是由衝突以外的其他原因引起的。
調用 BatchUpdate 時,ADO 和提供者將產生對資料來源執行更新的 SQL 陳述式。下一步,調用 Recordset 的 Resync
方法,並且將 AffectRecords 參數設定為等於 adAffectGroup,將 ResyncValues 參數設定為等於
adResyncUnderlyingValues。Resync 方法將用來自基本資料庫中的資料重新整理在當前 Recordset
對象中的資料。通過使用
adAffectGroup,可以確保只有使用當前篩選設定的情況下可見的記錄(即只有衝突記錄)會與資料庫重新同步。如果處理的是大型
Recordset,該操作會對效能有較大影響。通過在調用 Resync 時將 ResyncValues 參數設定為
adResyncUnderlyingValues,可以確保 UnderlyingValue 屬性將包含資料庫中的(衝突)值,並確保 Value
屬性仍然是由使用者輸入的值,還會確保 OriginalValue 屬性將持有欄位的原始值(即在上一次成功進行 UpdateBatch
調用之前該欄位所擁有的值)。然後,可以使用這些值通過編程方式解決衝突,或要求使用者選擇將要使用的值。
範例:
在調用 UpdateBatch 之前,本範例通過使用單獨的 Recordset 來更改基本表中的值,人為地建立一個衝突。
´Begin
On Error GoTo ErrHandler:
Dim objRs1 As New ADODB.Recordset
Dim objRs2 As New ADODB.Recordset
Dim strSQL As String
Dim strMsg As String
strSQL = "SELECT * FROM Shippers WHERE ShipperID = 2"’SQL查詢
objRs1.CursorLocation = adUseClient’設定用戶端資料指標
objRs1.Open strSQL, strConn, adOpenStatic, adLockBatchOptimistic, adCmdText’執行查詢產生objrs1記錄集
objRs1("Phone") = "(111) 555-1111"’更改表中一條記錄phone欄位的值
objRs2.Open strSQL, strConn, adOpenKeyset, adLockOptimistic, adCmdText’執行查詢產生objrs2記錄集
objRs2("Phone") = "(999) 555-9999" ’更改表中記錄phone欄位的值
objRs2.Update
objRs2.Close
Set objRs2 = Nothing
On Error Resume Next
objRs1.UpdateBatch
If objRs1.ActiveConnection.Errors.Count <> 0 Then
Dim intConflicts As Integer
intConflicts = 0
objRs1.Filter = adFilterConflictingRecords
intConflicts = objRs1.RecordCount
objRs1.Resync adAffectGroup, adResyncUnderlyingValues
If intConflicts > 0 Then
strMsg = "A conflict occurred with updates for " &
intConflicts & "records." & vbCrLf & "The values will be
restored" " to their original values."
objRs1.MoveFirst
While Not objRs1.EOF
strMsg = strMsg & "SHIPPER = " & objRs1("CompanyName") & vbCrLf
strMsg = strMsg & "Value = " & objRs1("Phone").Value & vbCrLf
strMsg = strMsg & "UnderlyingValue = " & _
objRs1("Phone").UnderlyingValue & vbCrLf
strMsg = strMsg & "OriginalValue = " & _
objRs1("Phone").OriginalValue & vbCrLf
strMsg = strMsg & vbCrLf & "Original value has been restored."
MsgBox strMsg, vbOKOnly, _
"Conflict " & objRs1.AbsolutePosition & _
" of " & intConflicts
objRs1("Phone").Value = objRs1("Phone").OriginalValue
objRs1.MoveNext
Wend
objRs1.UpdateBatch adAffectGroup
Else
strMsg = "Errors occurred during the update. " & _
objRs1.ActiveConnection.Errors(0).Number & " " & _
objRs1.ActiveConnection.Errors(0).Description
End If
On Error GoTo 0
End If
objRs1.MoveFirst
objRs1.Close
Set objRs1 = Nothing
Exit Sub
ErrHandler:
If Not objRs1 Is Nothing Then
If objRs1.State = adStateOpen Then objRs1.Close
Set objRs1 = Nothing
End If
If Not objRs2 Is Nothing Then
If objRs2.State = adStateOpen Then objRs2.Close
Set objRs2 = Nothing
End If
If Err <> 0 Then
MsgBox Err.Source & "-->" & Err.Description, , "Error"
End If
´End
還可以使用當前 Record 的或特定 Field 的 Status 屬性來確定已發生的衝突種類。
至此發生衝突的記錄檢查出來了,那麼下一步就是處理失敗的更新。
如何解決錯誤將取決於錯誤的性質和嚴重性以及應用程式的邏輯。實際中的資料庫大都數是多個使用者共用,典型的錯誤是其他人在你修改某個欄位之前先修改了該欄位。這種類型的錯誤稱為“衝突”。ADO 將檢測這種情況並報告錯誤。
如果存在更新錯誤,將被錯誤處理常式捕獲。通過使用 adFilterConflictingRecords 常量來篩選 Recordset,可以只顯示發生衝突行。
範例:
本錯誤解決方案將列印發生衝突的記錄中的作者的名和姓(au_fname 和 au_lname)。
´Begin:
objRs.Filter = adFilterConflictingRecords
objRs.MoveFirst
Do While Not objRst.EOF
Debug.Print "Conflict: Name = "; objRs!au_fname; " "; objRs!au_lname
objRs.MoveNext
Loop
´END
如果上述情況用事務來處理的話,那麼對於使用者數量較大網路較繁忙且更新記錄數很多的情況來說將嚴重影響速度。上述方法只要處理髮生錯誤的記錄,這樣就減輕了網路傳輸的負荷,有利提高速度。