一直很不順利,老是出現:
"Table unkown 'TableName'"
於是丟了個SQLMonitor去除錯
結果從SQLMonitor查到的語法是這樣的:
update "TableName" set 'Field' = ? where set 'Field' = ?
看起來沒錯?錯了!
在Firebird 2.1下,tablename是不行有任何引號的。
所以,問題就這樣出來了。
可是以前怎麼沒遇到?
原來以前都是自己下SQL語法,難怪都沒發生問題。
現在要偷懶叫tDataSetProvider來處理卻問題一堆。
DataSnap你好樣的…
最後,再附上以前的前輩是怎麼處理這個問題的。
來源:更新多個資料表的方法
更新多個資料表的方法
問題描述
使用 Delphi5 + BDE + MIDAS
我希望能一次更新兩個以上的資料表,可是這些異動必須在同一個交易中,
也就是其中任何一個資料表發生錯誤時必須全部rollback,應該怎麼做?
典型的場景:
假設有一個訂單管理程式,當新增一筆訂單時需要同時更新客戶資料與訂單資料。
一般的做法是在 DataSetProvider 的 BeforeUpdateRecord 事件中加入更新其他資料表的程式碼,例如:
1 2 3 4 5 6 7 8 | 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
單元,程式碼可以參考下面的範例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | //-----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 事件中撰寫更新其他資料表的程式碼,像這樣:
1 2 3 4 5 6 7 8 | 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 事件中自行處理掉所有
的更新的工作。例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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 你已經完成更新資料的工作。
2010/04/01 更新
我用了實體SQLDataSet+provider+clientdataset又重試了一下之前的問題
發現又正常了
運作的內容是:
1 2 3 4 | update TableName set Field = ? where Field = ? |
沒有留言:
張貼留言