跳到主要内容

เนื้อหาภาษาไทย / 泰语内容

系统指南 内容已审核

การจัดการ Transaction ด้วย PDO

การจัดการ Transaction ด้วย PDO

ในบทเรียนก่อนหน้านี้ เราได้เรียนรู้เกี่ยวกับการสร้าง, อ่าน, แก้ไข, และลบข้อมูล (CRUD Operations) โดยใช้ PDO (PHP Data Objects) ในบทนี้ เราจะมาเรียนรู้เกี่ยวกับการจัดการ Transaction ซึ่งเป็นเครื่องมือสำคัญในการรักษาความถูกต้องของข้อมูลเมื่อมีการดำเนินการหลายอย่างที่เกี่ยวข้องกันในฐานข้อมูล

Transaction คืออะไร?

Transaction คือลำดับของการดำเนินการ (operations) ที่ถือว่าเป็นหน่วยการทำงานเดียว (atomic unit) ซึ่งหมายความว่าการดำเนินการทั้งหมดใน Transaction จะต้องสำเร็จ (commit) หรือล้มเหลวทั้งหมด (rollback) หากมีส่วนใดส่วนหนึ่งของ Transaction ล้มเหลว การเปลี่ยนแปลงทั้งหมดที่เกิดขึ้นก่อนหน้านั้นจะถูกยกเลิก เพื่อให้ฐานข้อมูลกลับสู่สถานะเดิมก่อนที่จะเริ่ม Transaction

ลองนึกภาพว่าคุณกำลังโอนเงินจากบัญชีหนึ่งไปยังอีกบัญชีหนึ่ง การโอนเงินนี้ประกอบด้วยสองขั้นตอน:

  1. ลดจำนวนเงินในบัญชีต้นทาง
  2. เพิ่มจำนวนเงินในบัญชีปลายทาง

หากขั้นตอนแรกล้มเหลว (เช่น บัญชีต้นทางมีเงินไม่พอ) แต่ขั้นตอนที่สองสำเร็จ เงินก็จะหายไปจากระบบ! Transaction ช่วยป้องกันปัญหานี้ได้ โดยถ้าขั้นตอนแรกล้มเหลว ขั้นตอนที่สองจะไม่ถูกดำเนินการ และบัญชีทั้งสองจะอยู่ในสถานะเดิม

ทำไมต้องใช้ Transaction?

Transaction มีประโยชน์อย่างมากในการรักษาความสอดคล้องของข้อมูล (data consistency) ในสถานการณ์ที่ซับซ้อน ซึ่งเกี่ยวข้องกับการดำเนินการหลายอย่างที่ต้องเกิดขึ้นพร้อมกันหรือไม่มีเลย ตัวอย่างเช่น:

  • การโอนเงินระหว่างบัญชี (ดังที่กล่าวมา)
  • การสั่งซื้อสินค้าออนไลน์ (ต้องบันทึกข้อมูลการสั่งซื้อ, ลดจำนวนสินค้าคงคลัง, และบันทึกการชำระเงิน)
  • การลงทะเบียนผู้ใช้ (ต้องบันทึกข้อมูลผู้ใช้, สร้างบัญชี, และส่งอีเมลยืนยัน)

หากไม่มี Transaction และเกิดข้อผิดพลาดระหว่างการดำเนินการ อาจทำให้ข้อมูลในฐานข้อมูลไม่ถูกต้องหรือไม่สมบูรณ์

การเริ่มต้น Transaction

ในการเริ่มต้น Transaction ด้วย PDO เราจะใช้เมธอด beginTransaction() ของอ็อบเจ็กต์ PDO:

try {
    // สร้างการเชื่อมต่อ PDO (สมมติว่า $pdo ถูกสร้างไว้แล้ว)

    // เริ่มต้น Transaction
    $pdo->beginTransaction();

    // ดำเนินการ SQL queries ที่เกี่ยวข้องกับ Transaction
    // ...

} catch (PDOException $e) {
    // เกิดข้อผิดพลาด
    // ...
}

โค้ดนี้จะเริ่มต้น Transaction บนการเชื่อมต่อฐานข้อมูลที่ระบุ เมื่อ Transaction เริ่มต้นแล้ว การเปลี่ยนแปลงใดๆ ที่เกิดขึ้นกับฐานข้อมูลจะไม่ถูกบันทึกถาวร จนกว่าเราจะ commit Transaction

การ Commit Transaction

หากการดำเนินการทั้งหมดใน Transaction สำเร็จ เราจะบันทึกการเปลี่ยนแปลงทั้งหมดลงในฐานข้อมูลอย่างถาวร โดยใช้เมธอด commit():

try {
    // สร้างการเชื่อมต่อ PDO (สมมติว่า $pdo ถูกสร้างไว้แล้ว)

    // เริ่มต้น Transaction
    $pdo->beginTransaction();

    // ดำเนินการ SQL queries ที่เกี่ยวข้องกับ Transaction
    $stmt1 = $pdo->prepare("UPDATE accounts SET balance = balance - :amount WHERE account_id = :account_from");
    $stmt1->bindParam(':amount', $amount, PDO::PARAM_INT);
    $stmt1->bindParam(':account_from', $accountFrom, PDO::PARAM_INT);
    $stmt1->execute();

    $stmt2 = $pdo->prepare("UPDATE accounts SET balance = balance + :amount WHERE account_id = :account_to");
    $stmt2->bindParam(':amount', $amount, PDO::PARAM_INT);
    $stmt2->bindParam(':account_to', $accountTo, PDO::PARAM_INT);
    $stmt2->execute();


    // Commit Transaction
    $pdo->commit();

    echo "Transaction สำเร็จ!";

} catch (PDOException $e) {
    // เกิดข้อผิดพลาด
    // ...
}

หาก commit() สำเร็จ การเปลี่ยนแปลงทั้งหมดที่เกิดขึ้นระหว่าง beginTransaction() และ commit() จะถูกบันทึกถาวรในฐานข้อมูล

การ Rollback Transaction

หากมีการดำเนินการใดๆ ใน Transaction ล้มเหลว เราจะยกเลิกการเปลี่ยนแปลงทั้งหมดที่เกิดขึ้นก่อนหน้านี้ โดยใช้เมธอด rollback():

try {
    // สร้างการเชื่อมต่อ PDO (สมมติว่า $pdo ถูกสร้างไว้แล้ว)

    // เริ่มต้น Transaction
    $pdo->beginTransaction();

    // ดำเนินการ SQL queries ที่เกี่ยวข้องกับ Transaction
    $stmt1 = $pdo->prepare("UPDATE accounts SET balance = balance - :amount WHERE account_id = :account_from");
    $stmt1->bindParam(':amount', $amount, PDO::PARAM_INT);
    $stmt1->bindParam(':account_from', $accountFrom, PDO::PARAM_INT);
    $stmt1->execute();

    //จำลองการเกิดข้อผิดพลาด
    if($amount > 10000){
        throw new Exception("จำนวนเงินมากเกินไป");
    }

    $stmt2 = $pdo->prepare("UPDATE accounts SET balance = balance + :amount WHERE account_id = :account_to");
    $stmt2->bindParam(':amount', $amount, PDO::PARAM_INT);
    $stmt2->bindParam(':account_to', $accountTo, PDO::PARAM_INT);
    $stmt2->execute();


    // Commit Transaction
    $pdo->commit();

    echo "Transaction สำเร็จ!";

} catch (PDOException | Exception $e) {
    // เกิดข้อผิดพลาด
    echo "Transaction ล้มเหลว: " . $e->getMessage();

    // Rollback Transaction
    $pdo->rollBack();
}

หาก rollback() ถูกเรียกใช้ การเปลี่ยนแปลงทั้งหมดที่เกิดขึ้นระหว่าง beginTransaction() และ rollback() จะถูกยกเลิก และฐานข้อมูลจะกลับสู่สถานะเดิมก่อนที่จะเริ่ม Transaction

ตัวอย่างการใช้งาน Transaction

ต่อไปนี้เป็นตัวอย่างที่สมบูรณ์ของการใช้ Transaction ในการโอนเงินระหว่างบัญชี:

<?php

// ข้อมูลการเชื่อมต่อฐานข้อมูล
$host = 'localhost';
$db   = 'testdb';
$user = 'root';
$pass = '';
$charset = 'utf8mb4';

$dsn = "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    PDO::ATTR_EMULATE_PREPARES   => false,
];

try {
    $pdo = new PDO($dsn, $user, $pass, $options);

    $accountFrom = 1; // รหัสบัญชีต้นทาง
    $accountTo = 2;   // รหัสบัญชีปลายทาง
    $amount = 500;    // จำนวนเงินที่ต้องการโอน

    // เริ่มต้น Transaction
    $pdo->beginTransaction();

    // ลดจำนวนเงินในบัญชีต้นทาง
    $stmt1 = $pdo->prepare("UPDATE accounts SET balance = balance - :amount WHERE account_id = :account_from");
    $stmt1->bindParam(':amount', $amount, PDO::PARAM_INT);
    $stmt1->bindParam(':account_from', $accountFrom, PDO::PARAM_INT);
    $stmt1->execute();

    // เพิ่มจำนวนเงินในบัญชีปลายทาง
    $stmt2 = $pdo->prepare("UPDATE accounts SET balance = balance + :amount WHERE account_id = :account_to");
    $stmt2->bindParam(':amount', $amount, PDO::PARAM_INT);
    $stmt2->bindParam(':account_to', $accountTo, PDO::PARAM_INT);
    $stmt2->execute();

    // Commit Transaction
    $pdo->commit();

    echo "Transaction สำเร็จ! โอนเงิน $amount จากบัญชี $accountFrom ไปยังบัญชี $accountTo";

} catch (PDOException $e) {
    // เกิดข้อผิดพลาด
    echo "Transaction ล้มเหลว: " . $e->getMessage();

    // Rollback Transaction
    $pdo->rollBack();
}

?>

ในตัวอย่างนี้ หากการลดจำนวนเงินในบัญชีต้นทางสำเร็จ แต่การเพิ่มจำนวนเงินในบัญชีปลายทางล้มเหลว (เช่น บัญชีปลายทางไม่มีอยู่) Transaction จะถูก rollback และการเปลี่ยนแปลงทั้งหมดจะถูกยกเลิก

สรุป

Transaction เป็นเครื่องมือที่มีประสิทธิภาพในการรักษาความถูกต้องของข้อมูลในฐานข้อมูล การใช้ Transaction ช่วยให้เราสามารถดำเนินการหลายอย่างที่เกี่ยวข้องกันได้อย่างปลอดภัย และมั่นใจได้ว่าข้อมูลในฐานข้อมูลจะสอดคล้องกันเสมอ ในบทเรียนถัดไป เราจะเรียนรู้เกี่ยวกับการตรวจสอบ (validation) ข้อมูลที่รับมาจากผู้ใช้ ก่อนที่จะนำไปบันทึกในฐานข้อมูล เพื่อป้องกันปัญหาต่างๆ ที่อาจเกิดขึ้น

Office.in.th Custom Service

想基于此结构开发或定制系统?

Office.in.th 承接企业级 Web Application、API Integration 以及政府/企业管理系统的设计与开发,并提供长期支持。

查看软件开发服务