Question: I wrote a database operation class TDBPerate_DL to unify the operations on the database. Methods for declaring transaction start, committing transaction, and rolling back transaction are provided for other classes to call. TDBOperate_DL = class PRivate ADOC:TADOConnection; ADOQ:TADOQuery; isDestroyADOC:Boolean; //Destroy your own ADOC? fIsInTrans:Boolean; //Whether the transaction has started public isCommit:Boolean; //Whether to commit the transaction, the default is true, if a class votes against submission, it is false 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:TADOConnection); end; Implementation of some functions: procedure TDBOperate_DL.BeginTrans ; //Start transaction begin self.ADOC.BeginTrans; self.fIsInTrans := true;end;procedure TDBOperate_DL.CommitTrans; //Commit transaction begin self.ADOC.CommitTrans; self.fIsInTrans := false;end;procedure TDBOperate_DL.RollbackTrans; //Rollback transaction begin self.ADOC.RollbackTrans ; self.fIsInTrans := false;end;function TDBPerate_DL.IsInTrans: Boolean; //Check whether the transaction has started begin result := self.fIsInTrans;end;Write a TThing class to add, modify or delete records about something to the database, call the TDBPerate_DL class to complete. For the convenience of calling, the relevant transactions are placed in the TThing class, and there is no need to consider the transaction when making external calls. For example: procedure Tthing.Drop(const thing:String);var sqlString:String;begin sqlString := deleted SQL statement; self.DBPerate.BeginTrans; // DBOperate is a private variable of TDBPerate_DL type, passed in when creating a Tthing class instance parameters. try self.DBOperate.Execute(sqlString); self.DBOperate.CommitTrans; except self.DBOperate.RollbackTrans; raise; end;end; Later, I wrote a TPerson class for adding, modifying or deleting information about people in the database Record. The same transaction is placed in the TPerson class. Now when I want to delete a person's record, I call the TThing class to delete things related to the person. A transaction problem arises: transactions cannot be nested. If you delete TThing first, and then re-declare the transaction to delete TPerson, if TPerson makes an error, how can you roll back TThing? For example: procedure Tperson.Drop(const person:String);var sqlString:String; thing:Tthing;begin sqlString := deleted SQL statement; thing := Tthing.Create(self.DBOperate); //The DBOperate of TDBOperate_DL type is Passed in as a parameter. Self.DBOperate.BeginTrans; Try Thing.Drop(person); //There is a transaction inside, see the code above Self.DBOperate.Execute(sqlString); self.DBOperate.CommitTrans; except self.DBOperate.RollbackTrans; raise; end;end ;Solution, two-phase submission, first some background knowledge: No matter the two-tier or three-tier system, transaction processing is realized through two-phase submission. In the first phase, each executed resource/record is written into the transaction environment (TranscationContext), and then the resource coordinator sequentially queries whether the execution of each participating transaction is successful. If there are no problems, it enters the second phase. Each execution starts by Commiting its operations. If there is a problem with one execution, the resource coordinator notifies all subordinate executions to give up Commit and restore the original state of the data. Referring to the transaction operation of COM+, if a component requires a transaction, the transaction has already started when the component is created. When the component is destroyed, a transaction vote is performed. If it is a root transaction, the transaction is committed or rolled back. (If the component supports pooling, these two situations occur in the component activation and sleep events). So we define a class as follows. //Ancestor class of business class, used to provide unified transaction support TTS_DL = class private isRootTrans:Boolean; //Whether it is a root transaction isNeedTrans:Boolean; //Whether a transaction is required public DBOperate:TDBOperate_DL; //Class that operates the database Instance procedure SetComplete; procedure SetAbort; constructor Create(const newDBOperate:TDBOperate_DL; needTrans:Boolean);//Whether transaction support destructor is required Destroy; override; end; When this class is created, in addition to passing in the instance of the class that operates the database, a flag is passed in to indicate whether a transaction is required, because if it is only an operation of reading the database, there is no need for a transaction. The class implementation code is as follows: constructor TTS_DL.Create(const newDBOperate: TDBOperate_DL; needTrans: Boolean);begin inherited Create; self.DBOperate := newDBOperate; self.isNeedTrans := needTrans; //Whether assignment requires a transaction if self.isNeedTrans then begin //If it is in a transaction, it is not the root transaction, and the value of isCommit in the transaction context remains unchanged if self.DBOperate.isInTrans then self.isRootTrans := false else begin self.DBOperate.BeginTrans; //If it is the root transaction, start the transaction self.isRootTrans := true; self.DBOperate.isCommit := true; //Initialize the commit flag To commit the transaction end; end;end;destructor TTS_DL.Destroy;begin if self.isNeedTrans then begin //If it is a root transaction, commit or rollback the transaction according to the voting results 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; //Vote to rollbackend;procedure TTS_DL.SetComplete;begin self.DBOperate.isCommit := self.DBOperate.isCommit And true; //Vote to commitend;Go back to the business classes Tthing and Tperson, this time All are inherited from the TTS_DL class. Tthing = class(TTS_DL); Tperson = class(TTS_DL); The deletion code of Tthing should be as follows: procedure Tthing.Drop(const thing:String);var sqlString:String;begin sqlString := deleted SQL statement; try self. DBOperate.Execute(sqlString); self. DBOperate.SetComplete; //Vote submission except self. DBOperate.SetAbort; //Vote rollback raise; end;end; Tperson's deletion code is as follows: procedure Tperson.Drop(const person:String);var sqlString:String; thing:Tthing;begin sqlString := deleted SQL statement; thing := Tthing.Create(self. DBOperate,true); //TDBOperate_DL type DBOperate is passed in as a parameter, true means a transaction is required. Try Try Thing.Drop(person); Self.DBOperate.Execute(sqlString); self.DBOperate.SetComplete; //Vote submission except self. DBOperate.SetAbort; //Vote rollback raise; end; finally thing.free; //Remember to release end;end; Remember to keep the only instance of the operating database class TDBOperate_DL used in the program, and remember to release the business class instance. If a transaction is required, release it as early as possible, OK, done. The first version has a limited level and needs to be improved in practical applications. It is just a way to attract new ideas. Please have experienced heroes to contribute :)