本文目录导读:

- 使用 PDO(PHP Data Objects)进行回滚
- 使用 MySQLi(MySQL Improved)进行回滚
- 使用 Laravel(Eloquent ORM)进行回滚
- 必须注意的关键点(避免常见错误)
- 完整示例:带日志和回滚的银行转账
- 总结操作流程
在PHP中实现数据回滚,主要依赖于数据库事务(Transaction),核心思路是:将多个数据库操作(增、删、改)组合成一个原子操作,要么全部成功(Commit),要么全部失败(Rollback)。
以下是三种主流方式的操作详解:
使用 PDO(PHP Data Objects)进行回滚
这是最推荐的方式,支持多种数据库(MySQL、PostgreSQL等)。
<?php
try {
// 1. 创建 PDO 连接(务必关闭自动提交)
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8mb4', 'root', 'password');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// 2. 关闭自动提交,开始事务
$pdo->beginTransaction();
// 3. 执行多个数据操作
// 操作1: 更新用户余额
$sql1 = "UPDATE users SET balance = balance - 100 WHERE id = 1";
$pdo->exec($sql1);
// 操作2: 插入订单记录
$sql2 = "INSERT INTO orders (user_id, amount, status) VALUES (1, 100, 'pending')";
$pdo->exec($sql2);
// 操作3: 检查用户余额是否足够(模拟业务逻辑检查)
$stmt = $pdo->query("SELECT balance FROM users WHERE id = 1");
$user = $stmt->fetch();
if ($user['balance'] < 0) {
// 余额不足,抛出异常触发回滚
throw new Exception("余额不足,需要回滚!");
}
// 4. 全部成功,提交事务
$pdo->commit();
echo "操作成功完成!";
} catch (Exception $e) {
// 5. 发生任何异常,回滚事务
$pdo->rollBack();
echo "操作失败,已回滚:" . $e->getMessage();
}
?>
关键点:
PDO::ATTR_ERRMODE设为异常模式,方便捕获错误。beginTransaction()自动关闭自动提交。rollBack()撤销 beginTransaction 之后的所有操作。
使用 MySQLi(MySQL Improved)进行回滚
适用于项目已使用 MySQLi 扩展的情况。
<?php
$mysqli = new mysqli("localhost", "root", "password", "test");
// 检查连接
if ($mysqli->connect_error) {
die("连接失败: " . $mysqli->connect_error);
}
// 设置自动提交为 false
$mysqli->autocommit(FALSE);
try {
// 操作1: 扣钱
$result1 = $mysqli->query("UPDATE users SET balance = balance - 100 WHERE id = 1");
if (!$result1) throw new Exception("扣钱失败");
// 操作2: 加钱到另一个账户
$result2 = $mysqli->query("UPDATE users SET balance = balance + 100 WHERE id = 2");
if (!$result2) throw new Exception("加钱失败");
// 全部成功 → 提交
$mysqli->commit();
echo "转账成功";
} catch (Exception $e) {
// 失败 → 回滚
$mysqli->rollback();
echo "转账失败,已回滚:" . $e->getMessage();
}
$mysqli->close();
?>
注意:MySQLi 的 autocommit(FALSE) 和 commit() / rollback() 手动控制事务。
使用 Laravel(Eloquent ORM)进行回滚
如果你使用 Laravel 框架,有更简洁的语法:
use Illuminate\Support\Facades\DB;
try {
DB::beginTransaction();
// 业务操作
$user = User::find(1);
$user->balance -= 100;
$user->save();
Order::create([
'user_id' => 1,
'amount' => 100
]);
// 手动检查并抛异常
if ($user->balance < 0) {
throw new \Exception("余额不足");
}
DB::commit();
return "操作成功";
} catch (\Exception $e) {
DB::rollBack();
return "操作失败,已回滚:" . $e->getMessage();
}
必须注意的关键点(避免常见错误)
| 问题 | 正确做法 |
|---|---|
| 引擎必须是 InnoDB | MyISAM 引擎不支持事务,操作不会回滚,检查表引擎:SHOW TABLE STATUS LIKE 'your_table' |
| 捕获所有异常 | 要捕获 Exception 或 Throwable,不能只捕获特定错误,否则未捕获的错误会跳过 rollback |
不要在事务中使用 die() 或 exit() |
会导致连接断开,事务自动回滚但可能不完整 |
| 不要在事务内部混合多层 try-catch | 确保一个事务对应一个 try-catch 块,否则可能误提交或重复回滚 |
| 注意嵌套事务 | PHP 原生 PDO 不原生支持嵌套事务(MySQL 也不支持),Laravel 等框架会模拟,但建议避免复杂的嵌套 |
完整示例:带日志和回滚的银行转账
<?php
function transferMoney($fromId, $toId, $amount) {
$pdo = new PDO('mysql:host=localhost;dbname=bank', 'root', 'password');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
try {
$pdo->beginTransaction();
// 扣款
$stmt = $pdo->prepare("UPDATE accounts SET balance = balance - ? WHERE id = ? AND balance >= ?");
$stmt->execute([$amount, $fromId, $amount]);
if ($stmt->rowCount() === 0) {
throw new Exception("扣款失败(余额不足或账户不存在)");
}
// 入账
$stmt = $pdo->prepare("UPDATE accounts SET balance = balance + ? WHERE id = ?");
$stmt->execute([$amount, $toId]);
if ($stmt->rowCount() === 0) {
throw new Exception("入账失败(收款账户不存在)");
}
// 记录日志
$pdo->exec("INSERT INTO transfer_log (from_id, to_id, amount, time) VALUES ($fromId, $toId, $amount, NOW())");
$pdo->commit();
return "转账成功";
} catch (Exception $e) {
$pdo->rollBack();
error_log("转账失败: " . $e->getMessage());
return "转账失败,交易已撤销";
}
}
总结操作流程
- 关闭自动提交 / 开始事务
- 执行所有业务操作
- 进行业务规则校验(余额检查、唯一性检查等)
- 全部成功 → 提交
- 任一失败 → 回滚
这就是 PHP 中实现数据回滚的核心方式,如果你使用的是特定框架(ThinkPHP、Symfony 等),通常都有类似 Db::transaction() 的便捷方法,原理相同。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播