- 註冊時間
- 2006-3-13
- 最後登錄
- 2025-1-10
- 在線時間
- 673 小時
- 閱讀權限
- 200
- 積分
- 417
- 帖子
- 1107
- 精華
- 0
- UID
- 2
  
|
問題描述
使用 Delphi5 + BDE + MIDAS
我希望能一次更新兩個以上的資料表,可是這些異動必須在同一個交易中,也就是其中任何一個資料表發生錯誤時必須全部rollback,應該怎麼做?
典型的場景
假設有一個訂單管理程式,當新增一筆訂單時需要同時更新客戶資料與訂單資料。一般的做法是在 DataSetProvider 的 BeforeUpdateRecord 事件中加入更新其他資料表的程式碼,例如:
- procedure TForm1.dspOrdersBeforeUpdateRecord(Sender: TObject; SourceDS: TDataSet; DeltaDS: TClientDataSet; UpdateKind: TUpdateKind; var Applied: Boolean);
- begin
- if UpdateKind = ukModify then
- begin
- qryUpdate.SQL.Text := 'update Customer set City="AAA" where CustNo="1221"';
- qryUpdate.ExecSQL;
- end;
- end;
複製代碼
以上面的例子來看,會先更新客戶資料 Customer,然後再更新訂單資
料 Orders。可是如果更新 Orders 時發生了錯誤,之前對於 Customer
所做的異動卻無法 rollback 了。即使這兩個資料集元件都連結到相同
的 TDatabase 元件也一樣。這樣一來仍然無法達到資料完整性。
解決方案 A:
使用 Dan Miser 的 MIDAS Essensial Pack。使用時需 uses CDSUtil
單元,程式碼可以參考下面的範例:
App server 端:
- procedure TMtsDmod.MyApplyUpdates(var vDeltaArray: OleVariant; vProviderArray: OleVariant);
- begin
- try
- CDSApplyUpdates(vDeltaArray, vProviderArray);
- SetComplete;
- except
- SetAbort;
- end;
- end;
複製代碼
Client 端:
- procedure TForm1.btnApplyUpdatesClick(Sender: TObject);
- var
- cdsArray: array [0..1] of TCLientDataSet;
- vDeltaArray: OleVariant;
- vProviderArray: OleVariant;
- begin
- cdsArray[0] := cdsPubs; // Master
- cdsArray[1] := cdsTitles; // Detail
- vDeltaArray := RetrieveDeltas(cdsArray);
- vProviderArray := RetrieveProviders(cdsArray);
- DCOMConnection1.AppServer.ApplyUpdates(vDeltaArray, vProviderArray);
- ReconcileDeltas(cdsArray, vDeltaArray);
- end;
複製代碼
解決方案 B:
不要讓 DataSetProvider 更新資料,讓其連接的資料集元件自行更新資料。首先, DataSetProvider 的 ResolveToDataSet 必須設為 True 。然後在 qryOrders 的 OnUpdateRecord 事件中撰寫更新其他資料表的程式碼,像這樣:
- procedure TForm1.qryOrdersUpdateRecord(DataSet: TDataSet; UpdateKind: TUpdateKind; var UpdateAction: TUpdateAction);
- begin
- if UpdateKind = ukModify then
- begin
- qryUpdate.SQL.Text := 'update Customer set City="AAA" where CustNo="1221"';
- qryUpdate.ExecSQL;
- end;
- end;
複製代碼
你可以故意讓 Orders 在更新時發生錯誤,看看 Customer 資料表中
CustNo 為 '1221' 的那筆記錄的 City 欄位是否被改為 'AAA',如果
不是的話就表示這個方法的確可行,你可以自行驗證看看。
解決方案 C:
在 dataset provider 的 BeforeUpdateRecord 事件中自行處理掉所有
的更新的工作。例如:
- procedure TForm1.dspOrdersBeforeUpdateRecord(Sender: TObject; SourceDS: TDataSet; DeltaDS: TClientDataSet; UpdateKind: TUpdateKind; var Applied: Boolean);
- begin
- qryCustomer.UpdateObject := usqlCustomer;
- usqlCustomer.SetParams(UpdateKind);
- usqlCustomer.ExecSQL(UpdateKind);
- qryOrders.UpdateObject := usqlOrders;
- usqlOrders.SetParams(UpdateKind);
- usqlOrders.ExecSQL(UpdateKind);
- Applied := True;
- end;
複製代碼
使用這個方法時別忘了最後要將 Applied 設為 True, 告訴 dataset provider 你已經完成更新資料的工作。 |
|