如前所述,MicrosoftJet不能鎖定單個的記錄,每次讀、寫以及鎖定的都是一頁數據,面不是一個記錄。根據記錄的大小,一頁可能包含多個記錄。當鎖定一個記錄時,就會鎖定那一頁上的所有記錄,如圖8.6所示。
在高並發應用程序中,要求對指定記錄提供暢通無阻的訪問。可以用不同的策略來實現設計。
1.使用Text類型。可以用Text數據類型把字段添加到表中,直到記錄長度超過1024個字節。由於Text是變長數據類型,必須顯式地用數據來填補字段以獲得一個定長格式。同時,這種技術可能會降低性能,因為如果每個記錄都占用2K磁盤空間,則數據庫的長度將會增加。因此,建議不要使用Text數據類型。
2.使用CHAR類型。SQLDDLCHAR是一種定長數據類型。如果使用這種類型,則可不必用數據來填補字段。使用CHAR數據類型是實現這種技術的最簡單而且保險的唯一方法。
3.使用開放鎖定。另一種策略是:在任何地方都使用開放方式鎖定。雖然開放式鎖定不能避免頁面鎖定,但它可以使記錄被鎖定的時間最短,因此降低了不需要鎖定的記錄也被鎖定的可能性。
4.使用自定義的鎖定方案。有時候,頁面鎖定不能滿足需要,而開放式鎖定也可能無法滿足需要。在這種情況下,可以考慮使用自定義的鎖定方案。當一個記錄被鎖定時,可以用一個鎖定表來標識。鎖定表存儲記錄的鍵值、鎖定狀態(鎖定或未鎖定)以及鎖定記錄的用戶名等。自定義鎖定方案的實現需要大量的設計、實現以及測試時間。在許多情況下,它不能與內置的MicrosoftJet的功能重復。例如,即使實現單記錄鎖定,處理基於多個表的記錄集的數據也是非常困難的,因為必須標識所有表,而這些表都包含必須鎖定的記錄。當自定義鎖定方案只影響幾個表,而且不是基於一個具有復雜連結和關系的數據模型時,使用自定義鎖定方案較為適宜。
頁面鎖定中的錯誤處理
當使用頁面鎖定時,必須在代碼中檢查是否成功地實現了鎖定。在開始時應禁止錯誤處理程序,然後嘗試完成初始化一個鎖定、檢查錯誤,最後允許錯誤處理程序。在多用戶環境下,所遇到的頁面鎖定錯誤有三種,見表。
錯誤代碼和出錯信息原因和建議的操作
3186不能存儲;當前正被機器<nane>上的<name>用戶鎖定當一個用戶試圖更新一頁,而這一頁包含了另一個用戶設置的讀鎖定時,就會發生這個錯誤。為了處理這個錯誤,可以先等待一會兒,然後重新存儲該記錄。也可以通知發生這個問題的用戶,並詢問他們是否想要重試該操作
3197因為和另一個用戶試圖同時改變同一個數據,所以MicrosoftJet數據庫引擎終止了該過程在打開記錄集或最後一次從記錄中讀取數據之後,另一個用戶對當前記錄作了更改,並使用Edit方法或Update方法時,就會產生這個錯誤。如果在使用Edit方法時產生了這個錯誤,就要用當前數據來刷新用戶的數據視圖然後再試一次Edit方法。如果這個錯誤是在使用update方法時發生的,則是因為使用了開放式鎖定,並且在使用Edit方法後記錄發生了變更。在這種情況下,應通知用戶,其他人已經改變了數據。可以顯示出當前數據並讓用戶選擇:是覆蓋其他用戶的變更,還是取消編輯
3260不能更新;當前,被機器<name>上的<name>用戶鎖定鎖定了包含當前記錄的頁面,並使用AddNew或Edlt方法時,就會發生這個錯誤。在使用Update方法來把一個記錄存儲到一個鎖定的頁面時,也會發生這個錯誤。在用戶存儲新的記錄,或者在使用了開放式鎖定後,另一個用戶又來鎖定該頁面時,就會發生這種情況。為了處理這個錯誤,可等待一會兒,然後再試一次存儲該記錄。可以將該問題通知用戶,並讓用戶決定是否要重試這個操作
用錯誤代碼和出錯信息可以測試記錄是否被鎖定。下面的過程可用來檢測當前記錄是否在一個鎖定的頁面上:
FunctionRecordLocked(rstAsRecordset)AsBoolean
DimblnLockAsBoolean
OnErrorGoToErrorHandler
blnLock=rst.LockEdits'保存LockEdits屬性的當前值
rst.LockEdits=True'設置保守式鎖定
rst.Edit'試著編輯記錄,如果記錄被鎖定,則會產生錯誤3197
RecordLocked=False
rst.CancelUpdate
rst.LockEdits=blnLock'恢復LockEdits屬性的值
EXitFUnction
ErrorHandler:
SelectCaseErr
Case3197:
ResumeNext
CaseElse
RecordLocked=True
ResumeNext
EXitFunction
EndSelect
EndFunction
該過程把記錄集的鎖定設置為保守式,並編輯記錄,以測試當前記錄是否在一個被鎖定的頁面上,如果記錄被鎖定,則會產生一個錯誤(3197)。該過程有一個參數,即rst,它是Recordset對象。調用該過程,將返回一個布爾值。為了調用該過程,可以在窗體上畫一個命令按鈕,然後編寫如下事件過程:
PrivateSubCommand1_Click()
DimdbsAsDatabase
DimtbAsRecordset
DimblAsBoolean
Setdbs=opendatabase("c:dbdirdbl.mdb",dbopenDynaset)
Settb=dbs.OpenRecordset("tablel",dbOpenTable)
bl=RecordLocked(tb)
EndSub
在該事件過程中,打開一個數據庫和該數據庫中的表,然後用記錄集作為參數調用上述過程,調用結果放在變量bl中。如果記錄被鎖定,則返回值為True,否則為False。->