程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 更多編程語言 >> Delphi >> DELPHI的異常及事務保護的常見問題淺析

DELPHI的異常及事務保護的常見問題淺析

編輯:Delphi
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;
  

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved