MIDAS 3:更新多個資料表的方法 問題描述 使用 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 你已經完成更新資料的工作。