問題提出:寫了一個資料庫操作的類別TDBOperate_DL,統一對資料庫的操作嘛。提供了聲明事務開始、提交事務和回滾事務的方法供其他類別呼叫。 TDBOperate_DL = class PRivate ADOC:TADOConnection; ADOQ:TADOQuery; isDestroyADOC:Boolean; //是否銷毀自己的ADOC? fIsInTrans:Boolean; //是否已經開始事務public isCommit:Boolean; //是否要提交事務,缺省是真,如果有類別投票說反對提交,就為假function IsInTrans:Boolean; constructor Create(const newADOC:TADOConnection );overload; constructor Create(const ServerName,DataBaseName,UserID,PassWord:String);overload; destructor Destroy;override; procedure BeginTrans; procedure CommitTrans; procedure RollbackTrans; procedure Execute(const sqlString:String); function GetDataset(const sqlString:String):_Recordset; function GetConnection:TADOConnection; procedure SetConnection(const newADOC:TAConnection); procedure TDBOperate_DL.BeginTrans; //開始交易begin self.ADOC.BeginTrans; self.fIsInTrans := true;end;procedure TDBOperate_DL.CommitTrans; //提交交易begin self.ADOC.CommitTrans; self.fIsInTrans := false;end;procedure TDFate_DL. //回滾事務begin self.ADOC.RollbackTrans; self.fIsInTrans := false;end;function TDBOperate_DL.IsInTrans: Boolean; //查看事務是否已開始begin result := self.fIsInTrans;end;寫了一個TThing類,用於向資料庫中新增、修改或刪除有關某種東西的記錄,呼叫TDBOperate_DL類別完成。為了呼叫方便,因此有關的事務就放在了TThing類別中,外部呼叫時不用考慮事務了。如:procedure Tthing.Drop(const thing:String);var sqlString:String;begin sqlString := 刪除的SQL語句; self.DBOperate.BeginTrans; // DBOperate是TDBOperate_DL類型的私有變數,在建立Tthing類別實例時傳入Tthing類實例時傳入的參數。 try self.DBOperate.Execute(sqlString); self.DBOperate.CommitTrans; except self. DBOperate.RollbackTrans; raise; end;end;後來又寫了一個TPerson類,用於在資料庫中新增、修改或刪除有關人的記錄。同樣事務放在了TPerson類別中,現在我想刪除人的記錄時順便調用TThing類別刪除和人有關的東西,事務問題就出現啦:事務不能嵌套啊。如果先刪除TThing,再重新宣告交易刪除TPerson,如果TPerson出錯,還怎麼回滾TThing?如:procedure Tperson.Drop(const person:String);var sqlString:String; thing:Tthing;begin sqlString := 刪除的SQL語句; thing := Tthing.Create(self.DBOperate); //TDBOperate_DL類型的DBOperate是作為參數傳進去的。 Self.DBOperate.BeginTrans; Try Thing.Drop(person); //裡面有事務見上面的程式碼Self.DBOperate.Execute(sqlString); self.DBOperate.CommitTrans; except self.DBOperate.RollbackTrans; raise; endend; ;解決方法,兩階段提交,先黏點背景知識:不論兩層或三層體系,事物處理都是透過兩階段提交實現的。在第一階段,每個執行的的資源/記錄被寫入事物環境(TranscationContext)中,然後資源協調者順序查詢每一個參與事物的執行是否成功,如果都沒有問題的話,就進入第二階段,每個執行都開始Commit它的操作。如果有一個執行有問題的話,資源協調者通知所有下屬的執行放棄Commit,恢復資料原狀態。參考COM+的事務運行,如果一個元件是需要事務的,那麼在元件創建時,事務就已經開始了,在元件銷毀時進行事務投票,如果是根事務,則進行提交或回滾事務。 (如果組件支援池化,這兩種情況發生在組件啟動和休眠事件中)。於是我們定義一個類別如下。 //業務類別的祖先類,用於提供統一的事務支援TTS_DL = class private isRootTrans:Boolean; //是否是根事務isNeedTrans:Boolean; //是否需要事務public DBOperate:TDBOperate_DL; //操作資料庫的類別的實例procedure SetComplete; procedure SetAbort; constructor Create(const newDBOperate:TDBOperate_DL;needTrans:Boolean);//是否需要事務支援destructor Destroy;override; end;在該類別建立時,除了傳遞進操作資料庫的類別的實例外,再傳入一個是否需要事務的標誌,因為如果是只做讀取資料庫的操作,就用不著事務了。類別實作程式碼如下:constructor TTS_DL.Create(const newDBOperate: TDBOperate_DL; needTrans: Boolean);begin inherited Create; self.DBOperate := newDBOperate; self.isNeedTrans := needTrans頭是否需要事務; //如果在事務裡,就不是根事務,保留事務上下文裡isCommit的值不變if self.DBOperate.isInTrans then self.isRootTrans := false else begin self.DBOperate.BeginTrans; //是根事務,就開始事務self.isRootTrans := true; self.DBOperate.isCommit := true; //初始化提交標誌為要提交事務end; end;end;destructor TTS_DL.Destroy;begin if self.isNeedTrans then begin //如果是根事務,就按照投票結果進行事務提交或回滾if self.isRootTrans then begin if self.DBOperate.isCommit then self.DBOperate. CommitTrans else self.DBOperate.RollbackTrans; end; end; inherited;end;procedure TTS_DL.SetAbort;begin self.DBOperate.isCommit := self.DBOperate.isCommit And false; //投票說要回滾end;procedure TTS_DL.SetComplete;begin self.DBOperatesCommatef.DBOperate. .isCommit And true; //投票說要提交end;回到剛才的業務類別Tthing和Tperson,這次都從TTS_DL類別繼承下來。 Tthing = class(TTS_DL); Tperson = class(TTS_DL);Tthing的刪除程式碼該如下:procedure Tthing.Drop(const thing:String);var sqlString:String;begin sqlString := 刪除的SQL語句; try self. DBOperate.Execute(sqlString); self. DBOperate.SetComplete; //投票提交except self. DBOperate.SetAbort; //投票回滾raise; end;end;Tperson的刪除程式碼如下:procedure Tperson.Drop(const person:String);var sqlString:String; thing:Tthing;begin sqlString := 刪除的SQL語句; thing := Tthing.Create(self. DBOperate,true); //TDBOperate_DL類型的DBOperate是作為參數傳進去的,true代表需要事務。 Try Try Thing.Drop(person); Self.DBOperate.Execute(sqlString); self.DBOperate.SetComplete; //投票提交except self. DBOperate.SetAbort; //投票回滾raise; end; finally thing.free; //記著一定要釋放end;end;記著保持程式中使用唯一的操作資料庫類別TDBOperate_DL的實例,記著釋放業務類別實例,如果是需要事務的,盡量早釋放,OK,搞定。第一個版本,水平有限,還需要在實際應用中完善,全當拋磚引玉,請有經驗的大俠們拍磚吧:)