1、Exit方法
原以為Exit方法執行後,會馬上退出過程,但真正做了一個例子來測試後,才讓我改變了想法。請看下面這
個例子,flag最後被賦值為'C'。
================================================== ==============================================
var
flag: string;
begin
try
flag := 'A';
Exit;
flag := 'B';
finally
flag := 'C';
end;
flag := 'D';
end;
================================================== ==============================================
分析:不論try子句如何結束,finally 子句總是被執行。 (多謝ylmg網友)
2、一個能讓整個系統停止運作的小問題
在資料庫系統設計中,經常使用事務操作來保證資料的完整性,但是設計不當,卻容易產生比較大的影響,下面舉個例子說明雖然資料完整性保證了,但是可能令到系統完全停止運作:
================================================== ==============================================
AdoConnection1.BeginTrans;
try
……
if application.MessageBox('是否確定刪除?', '詢問', MB_YESNO+MB_ICONQUESTION)<>IDYes then //(1)
begin
……
end;
Application.MessageBox('操作失敗', '警告', MB_OK); //(2)
AdoConnection1.CommitTrans;
except
Application.MessageBox('操作失敗', '警告', MB_OK); //(3)
AdoConnection1.RollbackTrans;
end;
================================================== ==============================================
分析:上面程式碼中的問題都是因為(1)、(2)、(3)的Application.MessageBox引起,但引起問題的並不是Application.MessageBox本身,而是它會將程式掛起,需要使用者介入後,才繼續執行後面的操作;如果用戶這時候離開了計算機,或者沒有對這些對話框進行確定操作的話,可想而知,整個系統因為這個事務沒有結束而通通處於等待狀態了。
為了避免這個問題,原則有二:
(1)、事務啟動後,無需使用者乾預,程式可以自動結束事務;
(2)、在事務裡面做時間最短的操作。
3、try...except...end結構
下面舉個例子來說明try結構,還是使用事務操作的例子:
有問題的程式碼:
================================================== ==============================================
try
……
AdoConnection1.BeginTrans;
……
AdoConnection1.CommitTrans;
except
AdoConnection1.RollbackTrans;
end;
================================================== ==============================================
分析:如果try之後到AdoConnection1.BeginTrans這段程式碼中出現異常,將跳到AdoConnection1.RollbackTrans執行,但是AdoConnection1因為出錯並沒有啟動事務,所以AdoConnection1.RollbackTrans執行時出錯了。
正確的程式碼: ================================================ ==================================================
AdoConnection1.BeginTrans;
try
……
……
AdoConnection1.CommitTrans;
except
AdoConnection1.RollbackTrans;
end;
================================================== ==============================================
總之,try的架構是用來保護異常的操作的,try...except之間的產生異常都會執行except...end之間的操作,設計try指令時一定要注意架構的合理性。
4.欺騙了自己的事務保護
在做資料庫應用軟體時,我們常常需要碰到下面的問題:對原有資料進行判斷,然後再做出相對應的修改。這個問題看似比較簡單,但是如果考慮到網路上還有別的人在使用同一個系統,那麼,你就不的不考慮可能被意外改變的問題了。我的同事比較粗心,雖然在我的提示下考慮了多用戶問題,但他還是寫下了有問題的程式碼:
================================================== ==============================================
var
adsTemp: TAdoDataSet;
isOk: boolean;
begin
adsTemp := TAdoDataSet.Create(self);
try
adsTemp.Connection := AdoConnection1;
adsTemp.CommandText := 'select fid, fnumber from tb1 where fid=120';
adsTemp.Open;
isOk := adsTemp.FieldByName('fnumber').AsInteger>100;
finally
adsTemp.Free;
end;
if not isOk then
Exit;
AdoConnection1.BeginTrans;
try
AdoConnection1.Execute('update tb1 set ffull=ffull + 1 from tb1 where fid=120';
……
……
AdoConnection1.CommitTrans;
except
AdoConnection1.RollbackTrans;
end;
end;
================================================== ==============================================
分析:不知大家看出問題來了沒有,在AdoConnection1.BeginTrans之前判斷數據,然後使用AdoConnection1.Execute改變數據,如果這個數據是共享的,那麼在判斷之後到AdoConnection1.BeginTrans之前的這段時間裡頭,tb1的資料可能已經發生了改變,這個事務保護是沒有用處的。
正確的方法是判斷和修改必須是同一份數據,下面示例了有兩個方法(區別在於啟動事務的位置不相同):
代碼1(使用事務保護以後,判斷的和修改的是相同資料):
================================================== ==============================================
var
adsTemp: TAdoDataSet;
isOk: boolean;
begin
AdoConnection1.BeginTrans;
try
adsTemp := TAdoDataSet.Create(self);
try
adsTemp.Connection := AdoConnection1;
adsTemp.CommandText := 'select fid, fnumber, ffull from tb1 where fid=120';
adsTemp.Open;
if adsTemp.FieldByName('fnumber').AsInteger>100 then
begin
adsTemp.Edit;
adsTemp.FieldByName('ffull').AsInteger := adsTemp.FieldByName('ffull').AsInteger + 1;
adsTemp.Post;
end;
finally
adsTemp.Free;
end;
AdoConnection1.CommitTrans;
except
AdoConnection1.RollbackTrans;
end;
end;
================================================== ==============================================
程式碼2(使用異常捕捉,假如判斷和修改的不是同一份數據,adsTemp.Post時會出現異常,這個是ADODataSet物件具有的特性):
================================================== ==============================================
var
adsTemp: TAdoDataSet;
isOk: boolean;
begin
adsTemp := TAdoDataSet.Create(self);
try
adsTemp.Connection := AdoConnection1;
adsTemp.CommandText := 'select fid, fnumber, ffull from tb1 where fid=120';
adsTemp.Open;
if adsTemp.FieldByName('fnumber').AsInteger>100 then
begin
AdoConnection1.BeginTrans;
try
adsTemp.Edit;
adsTemp.FieldByName('ffull').AsInteger := adsTemp.FieldByName('ffull').AsInteger + 1;
adsTemp.Post;
AdoConnection1.CommitTrans;
except
AdoConnection1.RollbackTrans;
end;
end;
finally
adsTemp.Free;
end;
end;