首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP PDO 事務處理完整實戰:從原理到異常捕獲的標準化範本

     編輯:PHP 相關知識

資料庫事務確保多步操作的原子性(全成或全敗),是金流轉帳、庫存扣款等關鍵業務的基石。本文基於 PDO + InnoDB,提供標準化事務範本,涵蓋連線配置、自動提交關閉、異常驅動回滾、驗證查詢全流程。

前置條件確認

  • MySQL 表引擎必須為 InnoDB(MyISAM 不支援事務)。
  • PHP 擴充 pdo_mysql 已啟用。
  • 資料庫使用者擁有目標表的 SELECT, INSERT, UPDATE, DELETE 權限。

標準化事務程式碼範本

<?php
// 1. 建立 PDO 連線並配置屬性
$dsn = 'mysql:host=localhost;dbname=your_db;charset=utf8mb4';
$user = 'db_user';
$pass = 'db_pass';

try {
    $pdo = new PDO($dsn, $user, $pass, [
        PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION, // 錯誤拋出 Exception
        PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        PDO::ATTR_EMULATE_PREPARES   => false,                  // 使用原生預處理
    ]);
} catch (PDOException $e) {
    exit('資料庫連線失敗:' . $e->getMessage());
}

// 2. 事務業務邏輯
try {
    // 關閉自動提交,開啟事務
    $pdo->beginTransaction();

    // 業務 SQL 1:用戶 A 加款
    $age = 10;
    $stmt1 = $pdo->prepare("UPDATE accounts SET balance = balance + ? WHERE username = ?");
    $stmt1->execute([$age, 'user1']);
    if ($stmt1->rowCount() === 0) {
        throw new Exception('用戶 user1 不存在或更新失敗');
    }

    // 業務 SQL 2:用戶 B 扣款(模擬可能失敗)
    $stmt2 = $pdo->prepare("UPDATE accounts SET balance = balance - ? WHERE username = ? AND balance >= ?");
    $stmt2->execute([$age, 'user2', $age]);
    if ($stmt2->rowCount() === 0) {
        throw new Exception('用戶 user2 餘額不足或不存在');
    }

    // 所有步驟成功,提交事務
    $pdo->commit();
    echo '轉帳成功';

} catch (Exception $e) {
    // 任何異常觸發回滾
    if ($pdo->inTransaction()) {
        $pdo->rollBack();
    }
    echo '操作失敗:' . $e->getMessage();
}

// 3. 驗證結果
$rows = $pdo->query("SELECT username, balance FROM accounts WHERE username IN ('user1','user2')")->fetchAll();
echo '
'; print_r($rows); echo '
'; ?>

關鍵技術點深度解析

技術點說明最佳實踐
ATTR_ERRMODE => EXCEPTION將 SQL 錯誤轉為 Exception,統一由 try-catch 捕獲永遠啟用,避免靜默失敗
beginTransaction()關閉自動提交,標記事務起始點業務邏輯前必呼叫
prepare() + execute()預處理語句防 SQL 注入,支援參數綁定所有動態 SQL 必用
rowCount()檢查影響列數,判斷業務邏輯是否生效配合業務規則拋出自訂異常
inTransaction()檢查是否處於事務中,避免重複 rollBack 報錯catch 區塊前必檢查

進階場景:儲存過程與巢狀事務模擬

MySQL 不支援巢狀事務,但可用 Savepoint 模擬:

<?php
$pdo->beginTransaction();
try {
    // 主流程
    $pdo->exec("SAVEPOINT sp1");
    try {
        // 子流程(可獨立回滾)
        $pdo->exec("UPDATE ... ");
        $pdo->exec("RELEASE SAVEPOINT sp1");
    } catch (Exception $e) {
        $pdo->exec("ROLLBACK TO SAVEPOINT sp1");
    }
    $pdo->commit();
} catch (Exception $e) {
    $pdo->rollBack();
}
?>

常見錯誤與排查

  • SQLSTATE[HY000]: General error: 2014:未關閉前一個查詢的游標,確保 fetchAll() 或徹底遍歷結果集。
  • 事務未生效:檢查表引擎 SHOW TABLE STATUS LIKE 'tbl_name' 確認 Engine=InnoDB。
  • 死鎖:多表更新時保持一致的鎖定順序(如按主鍵排序),設定 innodb_lock_wait_timeout。

採用此標準化範本,能將事務邏輯錯誤率降至最低,確保核心業務資料一致性。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved