【发布时间】:2017-02-09 20:34:23
【问题描述】:
好的,我正在使用 PHP 在 MySQL 中寻找“嵌套”事务的解决方案,正如您在 MySQL 文档中所知道的那样,事务中不可能有事务 (Mysql transactions within transactions)。我试图使用http://php.net/manual/en/pdo.begintransaction.php 中提出的 Database 类,但不幸的是,这对我来说是错误的,因为它的计数器范围是 object level 而不是 class level,来解决这个问题我创建了这个类(TransactionController),它的计数器(名为$nest)是静态的,它带来了使事务“线性”所需的类级别(我说的是“线性”:它显然是嵌套的,但如果你看起来很它不是嵌套的,那么事务将运行良好,你怎么看?(看最后的例子,车主)
class TransactionController extends \\PDO {
public static $warn_rollback_was_thrown = false;
public static $transaction_rollbacked = false;
public function __construct()
{
parent :: __construct( ... connection info ... );
}
public static $nest = 0;
public function reset()
{
TransactionController :: $transaction_rollbacked = false;
TransactionController :: $warn_rollback_was_thrown = false;
TransactionController :: $nest = 0;
}
function beginTransaction()
{
$result = null;
if (TransactionController :: $nest == 0) {
$this->reset();
$result = parent :: beginTransaction();
}
TransactionController :: $nest++;
return $result;
}
public function commit()
{
$result = null;
if (TransactionController :: $nest == 0 &&
!TransactionController :: $transaction_rollbacked &&
!TransactionController :: $warn_rollback_was_thrown) {
$result = parent :: commit();
}
TransactionController :: $nest--;
return $result;
}
public function rollback()
{
$result = null;
if (TransactionController :: $nest >= 0) {
if (TransactionController :: $nest == 0) {
$result = parent :: rollback();
TransactionController :: $transaction_rollbacked = true;
}
else {
TransactionController :: $warn_rollback_was_thrown = true;
}
}
TransactionController :: $nest--;
return $result;
}
public function transactionFailed()
{
return TransactionController :: $warn_rollback_was_thrown === true;
}
// to force rollback you can only do it from $nest = 0
public function forceRollback()
{
if (TransactionController :: $nest === 0) {
throw new \PDOException();
}
}
}
class CarData extends TransactionController {
public function insertCar()
{
try {
$this->beginTransaction();
... (operations) ...
$this->commit();
}
catch (\PDOException $e) {
$this->rollback();
}
}
}
class PersonData extends TransactionController {
public function insertPerson( $person=null )
{
try {
$this->beginTransaction();
... (operations) ...
$this->commit();
}
catch (\PDOException $e) {
$this->rollback();
}
}
}
class CarOwnerData extends TransactionController {
public function createOwner()
{
try {
$this->beginTransaction();
$car = new CarData();
$car->insertCar();
$person = new PersonData();
$person->insertPerson();
... (operations) ...
$this->commit();
}
catch (\PDOException $e) {
$this->rollback();
}
}
}
$sellCar = new CarOwnerData();
$sellCar->createOwner();
UPDATE1:static attribute $warn_rollback_was_thrown 被添加到 TransactionController 以警告事务在执行的某个时刻失败,但没有回滚。
UPDATE2:当事务在某个时刻失败时,您可以让代码仍然运行到最后或使用forceRollback() 明确停止它,例如,请参见以下代码:
<?php // inside the class PersonData
public function insertMultiplePersons( $arrayPersons )
{
try {
$this->beginTransaction();
if (is_array( $arrayPersons )) {
foreach ($arrayPersons as $k => $person) {
$this->insertPerson( $person );
if ($this->transactionFailed()) {
$this->forceRollback();
}
}
}
$this->commit();
}
catch (\PDOException $e) {
$this->rollback();
}
} ?>
【问题讨论】:
-
除了在
rollback之后缺少$nest的重置:嵌套事务应该支持:start trans 1, do something, start trans 2, rollback trans 2, optionally retry trans 2 or do something else, commit trans 1,你的代码不能支持这个(因为 mysql 不支持它)。如果您从不回滚,您的代码只会按预期工作。每当发生任何内部回滚时,您都需要跳转到 trans 1 的异常(您可以通过重新引发异常来执行此操作),否则在$person->insertPerson();失败后,代码... (operations) ...将在没有任何事务的情况下执行。 -
这个想法不是回滚或提交嵌套事务,只是在级别 0 处回滚或提交所有,您需要更好地理解代码。出于这个原因,我说:事务看起来像是嵌套的(php),但实际上在 mysql 执行中它不是嵌套的。看着挺(没有丢失重置,因为有一个
$transaction_rollbacked = true -
你的意思是,在你编辑之后它会:-P(在它真的没有之前,它只会调用 $parent->rollback,实际上,它仍然不起作用)。但至少现在很清楚你想要做什么。您的代码现在看起来更好了。我认为
$this->beginTransaction();应该是$parent->beginTransaction();(否则它永远不会调用pdo,而是做一个循环)。rollback中的TransactionController :: $nest--;必须在== 0的测试之前(否则它永远不会回滚),对于commit也是如此。并且forceRollback应该设置一个变量,您在rollback中检查并重新抛出... -
通过使用静态类变量,您刚刚阻止自己在任何给定请求期间拥有多个数据库连接(例如到不同的数据库)。
-
我将在下面发布一个答案,并附上我的意思的代码示例。
标签: php mysql sql pdo transactions