天天看點

[轉載紅魚兒]kbmmw 開發點滴:kbmMW用戶端送出事務的現場處理

多層應用中的事務處理,是必須的,如果處理不好,就會出現各種資料不同步的現象,無法投入使用。以前用ASTA實作的多層應用,是在用戶端利用ASTA機 制,将要送出的資料集、執行的SQL及SP,統統生成到一個腳本中,在ASTA是TAstaParamList,然後一次性送出到伺服器,在伺服器端在執 行這個腳本時,開啟事務,執行腳本,如果成功則Commit,失敗則Rollback。

kbmMW提供了更好的事務處理機制,即可以在伺服器端做事務處理(TkbmMWTransactionResolve)也可以在用戶端處理(TkbmMWClientTransactionResolve)。

在用戶端,隻要利用kbmMWClientTransactionResolve.Resolve(Query1,Query2,...)即可實作事務控制,此方法傳回True,表明事務送出成功。如果送出失敗,伺服器端自動Rollback,完成事務復原。

有幾個事件可以捕獲送出失敗資訊。

1.kbmMWClientQuery.OnResolveError事件

2.kbmMWClientTransactionResolve.onResolveError事件

此外,當kbmMWClientQuery在kbmMWClientTransactionResolve提時失敗時,kbmMWClientTransactionResolve會為kbmMWClientQuery生成具體的錯誤記錄的資訊,儲存在kbmMWClientQuery屬性中,該屬性是kbmMWMemTable類型。

利用這幾種方式,可以很好的處理事務失敗後的錯誤提示等進一步處理。

問題是當送出失賬時,會産生新的問題:

1.假設有T1,T2兩個實體表,需要使用者同時修改并送出

2.當使用者對T1,T2都做了修改後,用kbmMWClientTransactionResolve.Resolve(T1,T2)送出給伺服器,當t2送出失敗時,比如主鍵重複,問題就來了。

3.如果使用者修改了T2的主鍵重複錯誤,再次送出,這時,不會再産生送出錯誤。

4.重新打開T1,T2,會發現T1的修改丢失,而T2正常修改了。

這種情況,對使用者的操作過程來講,是大問題,明明改了業務資料,儲存時産生錯誤,按錯誤提示修正了錯誤資料,再儲存,系統儲存成功,再次調出這筆業務資料,使用者會發現,修改結果沒有被正确的儲存!

對開發者來說,就是要在事務送出失敗後,能恢複失敗前的現場,即要把T1,T2的Delta儲存起來,不能因為送出失敗,而毀掉T1,T2的修改!

試圖查找kbmMW提供現成的機制(其實,感覺不應該發生這樣的問題),沒有找到。于是想一個方法,就是Resolve前将T1,T2儲存起來,送出有錯誤再恢複。

用AllData方法不行:

代碼:

在BeforeResolve事件儲存

procedure TForm2.kbmMWClientTransactionResolver1BeforeResolve(Sender: TObject;

  const ADatasets: TkbmMWClientCursorArray);

begin

  v1:=kbmMWClientQuery1.AllData;

  v2:=kbmMWClientQuery2.AllData;

end;

//送出不成功,試圖恢複(這裡隻是測試,正常應恢複沒有出錯的Query,好能再一次送出)

procedure TForm2.Button2Click(Sender: TObject);

  if not

kbmMWClientTransactionResolver1.Resolve([kbmMWClientQuery1,kbmMWClientQuery2,kbmMWClientQuery3])

then begin

kbmMWClientQuery1.AllData:=v1;//這句出錯,提示非法的屬性值

kbmMWClientQuery2.AllData:=v2;

  end;

還有一種方法,就是把delta寫入流,送出失敗再恢複,不知是否可行?先放一下。

問題一定出在TkbmMWClientTransactionResolver.Resolve方法中,硬着頭皮跟蹤一下,咱這功力看大俠的套路,心驚膽顫,運氣還不錯,終于查到,問題出ProcessErrorTable方法中:

procedure TkbmMWCustomPooledCursor.ProcessErrorTable

在這個方法中,這裡做了CheckPoint,把Delta幹掉了!當送出的資料集沒有傳回ErrorTalbe時,系統認為這個資料集正确送出,是以做了CheckPoint。

// Check if no errortable defined.

if (FErrorTable.FieldCount=0) or

(FErrorTable.RecordCount<=0) then

CheckPoint;

exit;

原因找到了,一時間還想不出最好的修改方法來解決這個問題。試圖找到一個屬性來控制這種行為,也沒有找到。等xalion來幫助了!

[轉載紅魚兒]kbmmw 開發點滴:kbmMW用戶端送出事務的現場處理

和xalion讨論該問題,也沒有找到kbmMW已有的機制,對于上面提到的問題點,也可以确定。

再進一步分析一下ClientTransactionResolve.Resolve方法的執行過程,大體上分三步:

1.對送出的資料集,制作成Variant數組及Stream

2.将1生成的内容一次性送出到伺服器端并接收傳回的結果

3.利用傳回結果對每一個送出的資料集進行ErrorTable處理,問題就出在這一步上。

首先,kbmMW從傳回的結果,生成每個資料集的ErrorTable内容,然後根據ErrorTable,調用ProcessErrorTable方

法,ProcessErrorTable方法有兩個主要目的:如果無錯,則調用CheckPoint,取消這次提前的修改内容,有錯,不調用

CheckPoint,觸發OnResolveError事件。

對于一個資料集是否要Checkpoint,不應該取決于目前資料集是否有錯,而應該由本次提要的結果來決定,就是說:當事務成功,則每一個資料集都應該做Checkpoint,事務不成功,則對于無錯的資料集也不能Checkpoint!

按此想法,對ClientTransactionResolve.Resolve做了修改,測試上面遇到的問題,可以正确儲存使用者的修改。

修改方法是注冊掉原ProcessErrorTable的調用,然後增加下面的代碼,核心那行,我加黑了!

cnt:=0;

for i:=low(ADatasets) to high(ADatasets) do

with ADatasets[i] do

if not IsDataModified then continue;

v:=vResult[cnt];

inc(cnt);

if VarIsNull(v) then continue;

if VarArrayHighBound(v,1)>2 then

sFlags:=v[3]

else

sFlags:='';

NoCheckpointOnError:=(pos('NOCHECKPOINTONERROR',sFlags)>0);

DisableMasterDetail;

try

bm:=GetBookmark;

//如果整體事務成功或者目前表有錯誤

if Result or (ErrorTable.RecordCount>0) then

ProcessErrorTable(TransactionOperation<>mwtoResolve,NoCheckpointOnError);

finally

GotoBookmark(bm);

except

FreeBookmark(bm);

EnableMasterDetail;