- 註冊時間
 - 2006-3-13 
 - 最後登錄
 - 2025-7-23 
 - 在線時間
 - 675 小時 
 - 閱讀權限
 - 200 
 - 積分
 - 417 
 - 帖子
 - 1109 
 - 精華
 - 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 你已經完成更新資料的工作。 |   
 
  
 |