คำถาม: ฉันเขียนคลาสการดำเนินการฐานข้อมูล TDBPerate_DL เพื่อรวมการดำเนินการบนฐานข้อมูลเข้าด้วยกัน วิธีการประกาศการเริ่มต้นธุรกรรม การทำธุรกรรม และการย้อนกลับธุรกรรมมีไว้สำหรับคลาสอื่น ๆ ที่จะโทร TDBOperate_DL = คลาส ADOC ส่วนตัว: TADOConnection; ADOQ: TADOQuery; isDestroyADOC: Boolean; // ทำลาย ADOC ของคุณเอง? fIsInTrans:Boolean; // ไม่ว่าธุรกรรมจะเริ่มต้นแล้วก็ตาม isInTrans:Boolean; // ไม่ว่าจะทำธุรกรรม ค่าเริ่มต้นจะเป็นจริง หากคลาสโหวตคัดค้านการส่ง มันเป็นฟังก์ชันเท็จ IsInTrans:Boolean; TADOConnection ); โอเวอร์โหลด ; สร้างตัวสร้าง (const ServerName, DataBaseName, UserID, PassWord: String); โอเวอร์โหลด; destructor ทำลาย; แทนที่; CommitTrans; ขั้นตอน RollbackTrans; ขั้นตอนการดำเนินการ (const sqlString: String); ฟังก์ชั่น GetDataset (const sqlString: String): _Recordset; ฟังก์ชั่น GetConnection: TADOConnection; ; // เริ่มการทำธุรกรรมเริ่มต้นด้วยตนเอง ADOC.BeginTrans; self.fIsInTrans := true;end;ขั้นตอน TDBOperate_DL.CommitTrans; //ทำธุรกรรมเริ่มต้น self.ADOC.CommitTrans; = false;end;ขั้นตอน TDBOperate_DL.RollbackTrans; self.fIsInTrans := false;end;function TDBPerate_DL.IsInTrans: บูลีน; //ตรวจสอบว่าธุรกรรมได้เริ่มต้นแล้วหรือไม่ ผลลัพธ์ := self.fIsInTrans;end;เขียนคลาส TThing เพื่อเพิ่ม แก้ไข หรือลบบันทึกเกี่ยวกับบางสิ่งในฐานข้อมูล เรียกคลาส TDBPerate_DL เพื่อดำเนินการให้เสร็จสิ้น เพื่อความสะดวกในการโทร ธุรกรรมที่เกี่ยวข้องจะอยู่ในคลาส TThing และไม่จำเป็นต้องพิจารณาธุรกรรมเมื่อทำการโทรภายนอก ตัวอย่างเช่น: ขั้นตอน Tthing.Drop(const thing:String);var sqlString:String;begin sqlString := คำสั่ง SQL ที่ถูกลบ; self.DBPerate.BeginTrans; // DBOperate เป็นตัวแปรส่วนตัวประเภท TDBPerate_DL ที่ส่งผ่านเข้ามาเมื่อสร้าง Tthing พารามิเตอร์อินสแตนซ์ของคลาส ลอง self.DBOperate.Execute(sqlString); self.DBOperate.CommitTrans; ยกเว้น self.DBOperate.RollbackTrans; ปลาย; ปลาย; ธุรกรรมเดียวกันถูกวางไว้ในคลาส TPerson ตอนนี้เมื่อฉันต้องการลบบันทึกของบุคคล ฉันจะเรียกคลาส TThing เพื่อลบสิ่งต่าง ๆ ที่เกี่ยวข้องกับบุคคลนั้น ปัญหาธุรกรรมเกิดขึ้น: ธุรกรรมไม่สามารถซ้อนกันได้ หากคุณลบ TThing ก่อน แล้วจึงประกาศธุรกรรมอีกครั้งเพื่อลบ TPerson หาก TPerson เกิดข้อผิดพลาด คุณจะย้อนกลับ TThing ได้อย่างไร ตัวอย่างเช่น: ขั้นตอน Tperson.Drop(const person:String);var sqlString:String; thing:Tthing;begin sqlString := คำสั่ง SQL ที่ถูกลบ; thing := Tthing.Create(self.DBOperate); //The DBOperate of TDBOperate_DL type ถูกส่งผ่านเป็นพารามิเตอร์ Self.DBOperate.BeginTrans; Try Thing.Drop(person); //มีธุรกรรมอยู่ข้างใน ดูโค้ดด้านบน Self.DBOperate.Execute(sqlString); end;end ;โซลูชัน การส่งแบบสองเฟส ขั้นแรกให้ความรู้พื้นฐาน: ไม่ว่าจะเป็นระบบแบบ 2 ระดับหรือ 3 ระดับ การประมวลผลธุรกรรมจะเกิดขึ้นได้ผ่านการส่งแบบ 2 เฟส ในระยะแรก ทรัพยากร/บันทึกที่ดำเนินการแต่ละรายการจะถูกเขียนลงในสภาพแวดล้อมของธุรกรรม (TranscationContext) จากนั้นผู้ประสานงานทรัพยากรจะสอบถามตามลำดับว่าการดำเนินการของธุรกรรมที่เข้าร่วมแต่ละรายการสำเร็จหรือไม่ หากไม่มีปัญหา จะเข้าสู่ระยะที่สอง การดำเนินการเริ่มต้นด้วยการดำเนินการ หากมีปัญหากับการดำเนินการครั้งเดียว ผู้ประสานงานทรัพยากรจะแจ้งการดำเนินการรองทั้งหมดเพื่อยกเลิก Commit และกู้คืนสถานะดั้งเดิมของข้อมูล อ้างอิงถึงการดำเนินการธุรกรรมของ COM+ หากส่วนประกอบต้องการธุรกรรม ธุรกรรมได้เริ่มต้นแล้วเมื่อส่วนประกอบถูกทำลาย การลงคะแนนธุรกรรมจะดำเนินการ ย้อนกลับ (หากส่วนประกอบรองรับการรวมกลุ่ม สองสถานการณ์นี้จะเกิดขึ้นในการเปิดใช้งานส่วนประกอบและเหตุการณ์สลีป) ดังนั้นเราจึงกำหนดคลาสดังนี้ //คลาสบรรพบุรุษของคลาสธุรกิจ ใช้เพื่อให้การสนับสนุนธุรกรรมแบบรวม TTS_DL = คลาสส่วนตัว isRootTrans:Boolean; // ไม่ว่าจะเป็นธุรกรรมรูท isNeedTrans:Boolean; // ไม่ว่าธุรกรรมนั้นจะต้องใช้ DBOperate สาธารณะ: TDBOperate_DL หรือไม่; ดำเนินการขั้นตอนอินสแตนซ์ฐานข้อมูล SetComplete; ขั้นตอน SetAbort; สร้าง (const newDBOperate: TDBOperate_DL; needTrans: Boolean); // ไม่ว่าจำเป็นต้องมีตัวทำลายการสนับสนุนธุรกรรมหรือไม่ ทำลาย แทนที่ สิ้นสุด เมื่อคลาสนี้ถูกสร้างขึ้น นอกเหนือจากการส่งผ่านในอินสแตนซ์ของคลาสที่ดำเนินการฐานข้อมูลแล้ว แฟล็กจะถูกส่งผ่านเพื่อระบุว่าจำเป็นต้องมีธุรกรรมหรือไม่ เพราะหากเป็นเพียงการดำเนินการในการอ่าน ฐานข้อมูลไม่จำเป็นต้องทำธุรกรรม รหัสการใช้งานคลาสมีดังนี้: ตัวสร้าง TTS_DL.Create(const newDBOperate: TDBOperate_DL; needTrans: Boolean);begin inherited Create; self.DBOperate := newDBOperate; = needTrans; // ไม่ว่าการมอบหมายงานจะต้องทำธุรกรรมหรือไม่ .isNeedTrans จากนั้นเริ่มต้น //หากอยู่ในธุรกรรม ไม่ใช่ธุรกรรมรูท และค่าของ isCommit ในบริบทธุรกรรมยังคงไม่เปลี่ยนแปลงหาก self.DBOperate.isInTrans แล้วก็ self.isRootTrans := false else start self.DBOperate.BeginTrans; // หากเป็นธุรกรรมรูท ให้เริ่มธุรกรรม self.isRootTrans := true; เตรียมใช้งานการตั้งค่าสถานะการคอมมิต เพื่อคอมมิตการสิ้นสุดธุรกรรม end;end;destructor TTS_DL.Destroy;begin if self.isNeedTrans จากนั้นเริ่มต้น // หากเป็นธุรกรรมรูท ให้คอมมิตหรือย้อนกลับธุรกรรมตามผลการลงคะแนน ถ้า self.isRootTrans เริ่มต้น ถ้า self.DBOperate.isCommit สืบทอดมา สิ้นสุด ขั้นตอน TTS_DL.SetAbort; เริ่มต้น self.DBOperate.isCommit := self.DBOperate.isCommit และเท็จ; //Vote to rollbackend;procedure TTS_DL.SetComplete;begin self.DBOperate.isCommit := self.DBOperate.isCommit And true; //Vote to commitend;กลับไปที่คลาสธุรกิจ Tthing และ Tperson, คราวนี้ ทั้งหมดสืบทอดมาจาก คลาส TTS_DL Tthing = class(TTS_DL); Tperson = class(TTS_DL); รหัสการลบของ Tthing ควรเป็นดังนี้: ขั้นตอน Tthing.Drop(const thing:String);var sqlString:String;begin sqlString := คำสั่ง SQL ที่ถูกลบ; .DBOperate.Execute(sqlString); self.DBOperate.SetComplete; //ส่งโหวต DBOperate.SetAbort; //Vote rollback Raise; end;end; รหัสการลบของ Tperson เป็นดังนี้: ขั้นตอน Tperson.Drop(const person:String);var sqlString:String; thing:Tthing;begin sqlString := คำสั่ง SQL ที่ถูกลบ; := Tthing.Create (ตนเอง DBOperate จริง); //TDBOperate_DL ประเภท DBOperate ถูกส่งผ่านเป็นพารามิเตอร์ จริงหมายความว่าจำเป็นต้องมีธุรกรรม ลอง Thing.Drop(person); Self.DBOperate.Execute(sqlString); self.DBOperate.SetComplete; //Vote //อย่าลืมปล่อย end;end; อย่าลืมเก็บอินสแตนซ์ของคลาสฐานข้อมูลปฏิบัติการ TDBOperate_DL ที่ใช้ในโปรแกรมไว้ และอย่าลืมปล่อยอินสแตนซ์คลาสธุรกิจ หากจำเป็นต้องมีธุรกรรม ให้ปล่อยโดยเร็วที่สุด ตกลง เสร็จแล้ว. เวอร์ชันแรกมีระดับที่จำกัดและจำเป็นต้องได้รับการปรับปรุงในการใช้งานจริง มันเป็นเพียงวิธีในการดึงดูดแนวคิดใหม่ๆ โปรดมีฮีโร่ผู้มีประสบการณ์มามีส่วนร่วมด้วย :)