================================================================================================
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;