我正在使用PHP与PDO和InnoDB表。
我只希望代码允许一个用户提交的操作完成,用户可以取消或完成。但是在用户发布这两个操作的情况下,我希望其中一个请求失败并回滚(现在没有发生),两者都在完成无例外/错误。我认为在检查它之后删除该行就足够了。
$pdo = new PDO();
try {
$pdo->beginTransaction();
$rowCheck = $pdo->query("SELECT * FROM table WHERE id=99")->rowCount();
if ($rowCheck == 0)
throw new RuntimeException("Row isn't there");
$pdo->exec("DELETE FROM table WHERE id = 99");
// either cancel, which does one bunch of queries. if (isset($_POST['cancel'])) ...
// or complete, which does another bunch of queries. if (isset($_POST['complete'])) ...
// do a bunch of queries on other tables here...
$pdo->commit();
} catch (Exception $e) {
$pdo->rollback();
throw $e;
}
如何将取消/完成操作作为关键部分?第二次操作必须失败。
答案 0 :(得分:1)
代码很好,但有一个例外:将FOR UPDATE
添加到初始SELECT
。这应该足以阻止第二个按钮按下直到第一个DELETE
发生,从而导致第二个按钮“失败”。
https://dev.mysql.com/doc/refman/5.5/en/innodb-locking-reads.html
注意仅使用
SELECT FOR UPDATE
锁定要更新的行 禁用自动提交时(通过开始事务处理)START TRANSACTION
或将autocommit设置为0.如果是自动提交 启用后,与规范匹配的行不会被锁定。
答案 1 :(得分:1)
另一种解决方案只是为了完整性:
private function getLock() {
$lock = $this->pdo->query("SELECT GET_LOCK('my_lock_name', 5)")->fetchColumn();
if ($lock != "1")
throw new RuntimeException("Lock was not gained: " . $lock);
}
private function releaseLock() {
$releaseLock = $this->pdo->query("SELECT RELEASE_LOCK('my_lock_name')")->fetchColumn();
if ($releaseLock != "1")
throw new RuntimeException("Lock not properly released " . $releaseLock);
}