PDO (PHP Data Objects) 提供了統一的資料庫訪問介面,支援預處理語句、異常模式、事務控制等現代特性。相比傳統 mysql_* 函數,PDO 更安全、可移植,是目前 PHP 專案的推薦選擇。
PDO::ATTR_AUTOCOMMIT => false 或 $pdo->setAttribute(PDO::ATTR_AUTOCOMMIT, 0)。$pdo->beginTransaction()。exec()(增刪改)或 prepare()/execute()(參數化查詢)。try-catch 捕獲 PDOException,調用 $pdo->rollBack()。$pdo->commit()。$pdo->setAttribute(PDO::ATTR_AUTOCOMMIT, 1)。<?php
// 1. 建立連接,關閉自動提交,啟用異常模式
$dsn = "mysql:host=127.0.0.1;dbname=test_db;charset=utf8mb4";
$user = "root";
$pass = "password";
try {
$pdo = new PDO($dsn, $user, $pass, [
PDO::ATTR_AUTOCOMMIT => 0,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8mb4"
]);
} catch (PDOException $e) {
die("資料庫連接失敗:" . $e->getMessage());
}
// 2. 業務邏輯:張三轉賬 500 元給李四
try {
$pdo->beginTransaction();
$amount = 500;
$fromId = 1; // 張三
$toId = 3; // 李四
// 扣款
$sql = "UPDATE `account` SET `balance` = `balance` - :amt WHERE `id` = :id AND `balance` >= :amt";
$stmt = $pdo->prepare($sql);
$stmt->bindValue(':amt', $amount, PDO::PARAM_INT);
$stmt->bindValue(':id', $fromId, PDO::PARAM_INT);
$stmt->execute();
if ($stmt->rowCount() === 0) {
throw new PDOException("張三餘額不足或賬戶不存在");
}
// 入賬
$sql = "UPDATE `account` SET `balance` = `balance` + :amt WHERE `id` = :id";
$stmt = $pdo->prepare($sql);
$stmt->bindValue(':amt', $amount, PDO::PARAM_INT);
$stmt->bindValue(':id', $toId, PDO::PARAM_INT);
$stmt->execute();
if ($stmt->rowCount() === 0) {
throw new PDOException("李四賬戶不存在");
}
// 記錄流水(可選)
$sql = "INSERT INTO `transfer_log` (`from_id`, `to_id`, `amount`, `created_at`) VALUES (:from, :to, :amt, NOW())";
$stmt = $pdo->prepare($sql);
$stmt->execute([':from' => $fromId, ':to' => $toId, ':amt' => $amount]);
$pdo->commit();
echo "轉賬成功!";
} catch (PDOException $e) {
$pdo->rollBack();
echo "轉賬失敗:" . $e->getMessage();
} finally {
// 3. 恢復自動提交(可選,視連接復用策略)
$pdo->setAttribute(PDO::ATTR_AUTOCOMMIT, 1);
}
?>
MySQL 中僅 InnoDB 支援事務,MyISAM、Memory 等不支援。建表時指定 ENGINE=InnoDB,或執行 ALTER TABLE t ENGINE=InnoDB 遷移。
SQLSTATE[HY000]: General error: 1213 Deadlock found 可重試機制。InnoDB 預設 REPEATABLE READ(可重複讀),配合 MVCC 實現一致性非鎖定讀。如需更高隔離可執行:
$pdo->exec("SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED");
bindValue(值綁定)而非 bindParam(引用綁定),避免循環中變量引用導致的邏輯錯誤。| 模式 | 常量 | 適用場景 |
|---|---|---|
| 靜默 | PDO::ERRMODE_SILENT | 不推薦,需手動檢查 errorInfo() |
| 警告 | PDO::ERRMODE_WARNING | 開發調試,不中斷執行 |
| 異常 | PDO::ERRMODE_EXCEPTION | 生產環境推薦,配合 try-catch 統一處理 |
PDO 不原生支援嵌套事務,但可用 Savepoint 模擬:
$pdo->beginTransaction();
// ... 外層操作
$pdo->exec("SAVEPOINT sp1");
try {
// ... 內層操作
$pdo->exec("RELEASE SAVEPOINT sp1");
} catch (Exception $e) {
$pdo->exec("ROLLBACK TO SAVEPOINT sp1");
// 決定是否繼續外層事務
}
$pdo->commit();
PDO 事務處理的核心在於「關閉自動提交 → 開啟事務 → 業務操作 → 成功提交/失敗回滾 → 恢復自動提交」。配合預處理語句、異常模式、InnoDB 行級鎖,能構建高可靠、高併發的資料庫應用。實際專案中建議封裝 Db::transaction(callable $callback) 輔助方法,統一異常處理與日誌記錄,減少樣板代碼。