PHP PDO::rollBack 方法
最后修改于 2025 年 4 月 19 日
PDO::rollBack 方法用于撤销在事务期间所做的更改。它会将数据库恢复到事务开始之前的状态。
基本定义
PDO::rollBack 回滚当前事务。仅当数据库支持事务且自动提交已关闭时才有效。
语法:public PDO::rollBack(): bool
。成功返回 true,失败返回 false。如果不存在活动事务,则抛出 PDOException。
基本事务回滚示例
这显示了在发生错误时 rollBack 的最简单用法。
basic_rollback.php
<?php declare(strict_types=1); try { $pdo = new PDO('mysql:host=localhost;dbname=testdb', 'user', 'password'); $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $pdo->beginTransaction(); $pdo->exec("INSERT INTO users (name, email) VALUES ('John', 'john@example.com')"); // Simulate an error throw new Exception("Something went wrong"); $pdo->commit(); } catch (Exception $e) { $pdo->rollBack(); echo "Transaction rolled back: " . $e->getMessage(); }
这启动了一个事务,插入了一条记录,然后模拟了一个错误。catch 块调用 rollBack 来撤销插入操作。数据库保持不变。
嵌套事务与回滚
这演示了嵌套事务的回滚行为。
nested_rollback.php
<?php declare(strict_types=1); try { $pdo = new PDO('mysql:host=localhost;dbname=testdb', 'user', 'password'); $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); // Outer transaction $pdo->beginTransaction(); $pdo->exec("INSERT INTO logs (message) VALUES ('Starting process')"); try { // Inner transaction $pdo->beginTransaction(); $pdo->exec("UPDATE accounts SET balance = balance - 100 WHERE id = 1"); // Simulate error in inner transaction throw new Exception("Transfer failed"); $pdo->commit(); } catch (Exception $e) { $pdo->rollBack(); // Rolls back only the inner transaction echo "Inner transaction failed: " . $e->getMessage(); } $pdo->commit(); // Commits the outer transaction echo "Outer transaction completed"; } catch (PDOException $e) { $pdo->rollBack(); // Rolls back everything if outer transaction fails echo "Error: " . $e->getMessage(); }
这展示了嵌套事务。内部的 rollBack 只会撤销内部操作。外部事务仍然可以成功提交。
条件回滚示例
这演示了基于业务逻辑条件使用 rollBack。
conditional_rollback.php
<?php declare(strict_types=1); try { $pdo = new PDO('mysql:host=localhost;dbname=bank', 'user', 'password'); $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $pdo->beginTransaction(); // Withdraw from account 1 $pdo->exec("UPDATE accounts SET balance = balance - 200 WHERE id = 1"); // Check if balance went negative $stmt = $pdo->query("SELECT balance FROM accounts WHERE id = 1"); $balance = $stmt->fetchColumn(); if ($balance < 0) { $pdo->rollBack(); echo "Transaction rolled back due to insufficient funds"; } else { // Deposit to account 2 $pdo->exec("UPDATE accounts SET balance = balance + 200 WHERE id = 2"); $pdo->commit(); echo "Transaction completed successfully"; } } catch (PDOException $e) { $pdo->rollBack(); echo "Error: " . $e->getMessage(); }
当账户余额变为负数时,此代码执行条件回滚。业务逻辑决定是提交还是回滚事务。
带保存点的回滚
这展示了使用保存点进行部分事务回滚。
savepoint_rollback.php
<?php declare(strict_types=1); try { $pdo = new PDO('mysql:host=localhost;dbname=testdb', 'user', 'password'); $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $pdo->beginTransaction(); // First operation $pdo->exec("INSERT INTO orders (product, quantity) VALUES ('Laptop', 1)"); $pdo->exec("SAVEPOINT point1"); // Second operation $pdo->exec("UPDATE inventory SET stock = stock - 1 WHERE product = 'Laptop'"); // Check stock level $stmt = $pdo->query("SELECT stock FROM inventory WHERE product = 'Laptop'"); $stock = $stmt->fetchColumn(); if ($stock < 0) { $pdo->exec("ROLLBACK TO SAVEPOINT point1"); echo "Partial rollback performed, order kept but inventory not updated"; } $pdo->commit(); } catch (PDOException $e) { $pdo->rollBack(); echo "Error: " . $e->getMessage(); }
这在第一个操作后创建一个保存点。如果第二个操作的检查失败,它将回滚到保存点,而不是整个事务。
批量处理中的回滚
这演示了在处理批量记录时使用 rollBack。
batch_rollback.php
<?php declare(strict_types=1); try { $pdo = new PDO('mysql:host=localhost;dbname=testdb', 'user', 'password'); $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $records = [ ['name' => 'Alice', 'email' => 'alice@example.com'], ['name' => 'Bob', 'email' => 'bob@example.com'], ['name' => '', 'email' => 'invalid'], // Invalid record ['name' => 'Charlie', 'email' => 'charlie@example.com'] ]; $pdo->beginTransaction(); $stmt = $pdo->prepare("INSERT INTO users (name, email) VALUES (?, ?)"); foreach ($records as $record) { if (empty($record['name']) || empty($record['email'])) { $pdo->rollBack(); throw new Exception("Invalid record found, rolling back entire batch"); } $stmt->execute([$record['name'], $record['email']]); } $pdo->commit(); echo "Batch processed successfully"; } catch (Exception $e) { echo "Error: " . $e->getMessage(); }
这在一个事务中处理多条记录。如果任何记录无效,则回滚整个批量。这确保了数据的一致性。
多数据库回滚
这展示了如何处理跨多个数据库连接的回滚。
multi_db_rollback.php
<?php declare(strict_types=1); try { // First database connection $pdo1 = new PDO('mysql:host=localhost;dbname=db1', 'user', 'password'); $pdo1->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); // Second database connection $pdo2 = new PDO('mysql:host=localhost;dbname=db2', 'user', 'password'); $pdo2->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $pdo1->beginTransaction(); $pdo2->beginTransaction(); try { $pdo1->exec("INSERT INTO orders (product) VALUES ('Phone')"); $orderId = $pdo1->lastInsertId(); $pdo2->exec("INSERT INTO shipments (order_id) VALUES ($orderId)"); // Simulate error throw new Exception("Shipping service unavailable"); $pdo1->commit(); $pdo2->commit(); } catch (Exception $e) { $pdo1->rollBack(); $pdo2->rollBack(); echo "Distributed transaction rolled back: " . $e->getMessage(); } } catch (PDOException $e) { echo "Connection error: " . $e->getMessage(); }
这协调了两个数据库之间的事务。如果任何操作失败,两个事务都将被回滚。这维护了跨系统的 一致性。
带错误日志的回滚
这演示了在回滚事务时记录错误。
logging_rollback.php
<?php declare(strict_types=1); try { $pdo = new PDO('mysql:host=localhost;dbname=testdb', 'user', 'password'); $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $pdo->beginTransaction(); $pdo->exec("UPDATE products SET stock = stock - 5 WHERE id = 101"); $pdo->exec("INSERT INTO order_items (product_id, quantity) VALUES (101, 5)"); // Check if product exists $stmt = $pdo->query("SELECT COUNT(*) FROM products WHERE id = 101"); if ($stmt->fetchColumn() == 0) { $pdo->rollBack(); // Log the error $errorMsg = "Attempted to order non-existent product 101"; $pdo->exec("INSERT INTO error_log (message) VALUES ('$errorMsg')"); throw new Exception($errorMsg); } $pdo->commit(); echo "Order processed successfully"; } catch (Exception $e) { echo "Error: " . $e->getMessage(); }
这回滚了主事务,但将错误记录到单独的表中。错误日志记录发生在回滚之后,以确保它始终被记录。
rollBack 的最佳实践
- 错误处理: 始终在事务中使用 try-catch。
- 事务范围: 保持事务尽可能短。
- 验证: 在开始事务之前验证数据。
- 嵌套事务: 理解特定于数据库的行为。
- 测试: 彻底测试回滚场景。
来源
本教程介绍了 PDO::rollBack 方法,并提供了实际示例,展示了事务回滚的各种必要场景。
作者
列出 所有 PHP PDO 函数。