• MySQL 事务嵌套


    1. MySQL默认自动提交(autocommit=1),如果要开启事务,先关闭自动提交(autocommit=0);
    2. InnoDB支持事务,MyISAM不支持;
    3. MySQL不支持事务嵌套;已经开启事务后,再开启事务(start transaction),会隐式的提交(commit)上一个事务。

    MySQL中可以通过部分事务(savepoint)来实现嵌套

    TRUNCATE nested_transaction;
    BEGIN;
    INSERT INTO `nested_transaction`(id) VALUES(1);
    SAVEPOINT t1;
    INSERT INTO `nested_transaction`(id) VALUES(2);
    ROLLBACK TO SAVEPOINT t1;
    COMMIT;

    id
    --------
    1

    PHP中Laravel中事务嵌套(ManagesTransactions.php)

    <?php
    
    namespace IlluminateDatabaseConcerns;
    
    use Closure;
    use Exception;
    use Throwable;
    
    trait ManagesTransactions
    {
        /**
         * Execute a Closure within a transaction.
         *
         * @param  Closure  $callback
         * @param  int  $attempts
         * @return mixed
         *
         * @throws Exception|Throwable
         */
        public function transaction(Closure $callback, $attempts = 1)
        {
            for ($currentAttempt = 1; $currentAttempt <= $attempts; $currentAttempt++) {
                $this->beginTransaction();
    
                // We'll simply execute the given callback within a try / catch block and if we
                // catch any exception we can rollback this transaction so that none of this
                // gets actually persisted to a database or stored in a permanent fashion.
                try {
                    return tap($callback($this), function ($result) {
                        $this->commit();
                    });
                }
    
                    // If we catch an exception we'll rollback this transaction and try again if we
                    // are not out of attempts. If we are out of attempts we will just throw the
                    // exception back out and let the developer handle an uncaught exceptions.
                catch (Exception $e) {
                    $this->handleTransactionException(
                        $e, $currentAttempt, $attempts
                    );
                } catch (Throwable $e) {
                    $this->rollBack();
    
                    throw $e;
                }
            }
        }
    
        /**
         * Handle an exception encountered when running a transacted statement.
         *
         * @param  Exception  $e
         * @param  int  $currentAttempt
         * @param  int  $maxAttempts
         * @return void
         *
         * @throws Exception
         */
        protected function handleTransactionException($e, $currentAttempt, $maxAttempts)
        {
            // On a deadlock, MySQL rolls back the entire transaction so we can't just
            // retry the query. We have to throw this exception all the way out and
            // let the developer handle it in another way. We will decrement too.
            if ($this->causedByDeadlock($e) &&
                $this->transactions > 1) {
                --$this->transactions;
    
                throw $e;
            }
    
            // If there was an exception we will rollback this transaction and then we
            // can check if we have exceeded the maximum attempt count for this and
            // if we haven't we will return and try this query again in our loop.
            $this->rollBack();
    
            if ($this->causedByDeadlock($e) &&
                $currentAttempt < $maxAttempts) {
                return;
            }
    
            throw $e;
        }
    
        /**
         * Start a new database transaction.
         *
         * @return void
         * @throws Exception
         */
        public function beginTransaction()
        {
            $this->createTransaction();
    
            ++$this->transactions;
    
            $this->fireConnectionEvent('beganTransaction');
        }
    
        /**
         * Create a transaction within the database.
         *
         * @return void
         */
        protected function createTransaction()
        {
            if ($this->transactions == 0) {
                try {
                    $this->getPdo()->beginTransaction();
                } catch (Exception $e) {
                    $this->handleBeginTransactionException($e);
                }
            } elseif ($this->transactions >= 1 && $this->queryGrammar->supportsSavepoints()) {
                $this->createSavepoint();
            }
        }
    
        /**
         * Create a save point within the database.
         *
         * @return void
         */
        protected function createSavepoint()
        {
            $this->getPdo()->exec(
                $this->queryGrammar->compileSavepoint('trans'.($this->transactions + 1))
            );
        }
    
        /**
         * Handle an exception from a transaction beginning.
         *
         * @param  Exception  $e
         * @return void
         *
         * @throws Exception
         */
        protected function handleBeginTransactionException($e)
        {
            if ($this->causedByLostConnection($e)) {
                $this->reconnect();
    
                $this->pdo->beginTransaction();
            } else {
                throw $e;
            }
        }
    
        /**
         * Commit the active database transaction.
         *
         * @return void
         */
        public function commit()
        {
            if ($this->transactions == 1) {
                $this->getPdo()->commit();
            }
    
            $this->transactions = max(0, $this->transactions - 1);
    
            $this->fireConnectionEvent('committed');
        }
    
        /**
         * Rollback the active database transaction.
         *
         * @param  int|null  $toLevel
         * @return void
         */
        public function rollBack($toLevel = null)
        {
            // We allow developers to rollback to a certain transaction level. We will verify
            // that this given transaction level is valid before attempting to rollback to
            // that level. If it's not we will just return out and not attempt anything.
            $toLevel = is_null($toLevel)
                ? $this->transactions - 1
                : $toLevel;
    
            if ($toLevel < 0 || $toLevel >= $this->transactions) {
                return;
            }
    
            // Next, we will actually perform this rollback within this database and fire the
            // rollback event. We will also set the current transaction level to the given
            // level that was passed into this method so it will be right from here out.
            $this->performRollBack($toLevel);
    
            $this->transactions = $toLevel;
    
            $this->fireConnectionEvent('rollingBack');
        }
    
        /**
         * Perform a rollback within the database.
         *
         * @param  int  $toLevel
         * @return void
         */
        protected function performRollBack($toLevel)
        {
            if ($toLevel == 0) {
                $this->getPdo()->rollBack();
            } elseif ($this->queryGrammar->supportsSavepoints()) {
                $this->getPdo()->exec(
                    $this->queryGrammar->compileSavepointRollBack('trans'.($toLevel + 1))
                );
            }
        }
    
        /**
         * Get the number of active transactions.
         *
         * @return int
         */
        public function transactionLevel()
        {
            return $this->transactions;
        }
    }
    View Code

    也可以只开启一个事物,判断如果是嵌套事务,则不做操作直接返回。

  • 相关阅读:
    shell if 条件语句实践
    shell函数
    透视财富增长的秘密
    kvm虚拟化实践
    Linux驱动编程--基于I2C子系统的I2C驱动
    Makefile中=、:=、+=、?=的区别
    字符设备驱动结构与开发
    驱动分类
    为什么ARM的frq中断的处理速度比较快
    Linux设备驱动01
  • 原文地址:https://www.cnblogs.com/zhoutianyuan/p/11113855.html
Copyright © 2020-2023  润新知