First, the origin of the problem
There is a clear explanation in the official MySQL documentation that nested transactions are not supported:
1. Transactions cannot be nested. This was a consequence of the implicit commit performed for any current transaction when you issue a START transaction stat Ement or one of its synonyms.
But when we develop a complex system, it is unavoidable to inadvertently nest transactions in the transaction, such as a function called the B function, a function uses a transaction, and is called in the transaction B function, B function also has a transaction, so there is a transaction nesting. In fact, the matter of a is of little significance, why? As mentioned in the above document, a simple translation is:
1. When a start transaction instruction is executed, a commit operation is performed implicitly. So we need to support the nesting of transactions at the system architecture level.
Fortunately, nested support has been made in some mature ORM frameworks, such as doctrine or laravel. Let's take a look at how these two frameworks are implemented. Friendly hints, the two framework of the function and the name of the variable is more intuitive, although it looks very long, but all through the name can directly know the meaning of this function or variable, so do not see such a big lump is frightened:)
Second, the doctrine solution
First look at the code that creates the transaction in doctrine (the irrelevant code is killed):
[PHP]View Plaincopy
- /**
- * Author http://www.lai18.com
- * Date 2015-04-19
- * Version 1
- **/
- Public function begintransaction ()
- {
- + +$this->_transactionnestinglevel;
- if ($this->_transactionnestinglevel = = 1) {
- $this->_conn->begintransaction ();
- } Else if ($this->_nesttransactionswithsavepoints) {
- $this->createsavepoint ($this->_getnestedtransactionsavepointname ());
- }
- }
The first line of this function uses a _transactionnestinglevel to identify the current level of nesting, if it is 1, that is not nested, then use the default method to execute the start transaction OK, if it is greater than 1, that is, there is nesting time, She's going to help us create a savepoint, which can be understood as a transaction record point that can only be rolled back to this point when a rollback is required. Then look at the rollback function:
[PHP]View Plaincopy
- 1. /**
- * Author http://www.lai18.com
- * Date 2015-04-19
- * Version 1
- **/
- Public function rollBack ()
- {
- if ($this->_transactionnestinglevel = = 0) {
- throw connectionexception::noactivetransaction ();
- }
- if ($this->_transactionnestinglevel = = 1) {
- $this->_transactionnestinglevel = 0;
- $this->_conn->rollback ();
- $this->_isrollbackonly = false;
- } Else if ($this->_nesttransactionswithsavepoints) {
- $this->rollbacksavepoint ($this->_getnestedtransactionsavepointname ());
- --$this->_transactionnestinglevel;
- } Else {
- $this->_isrollbackonly = true;
- --$this->_transactionnestinglevel;
- }
- }
You can see the way of processing is also very simple, if level is 1, direct rollback, otherwise roll back to the previous savepoint. Then we continue to look at the commit function:
[PHP]View Plaincopy
- 1. /**
- * Author http://www.lai18.com
- * Date 2015-04-19
- * Version 1
- **/
- Public function commit ()
- {
- if ($this->_transactionnestinglevel = = 0) {
- throw connectionexception::noactivetransaction ();
- }
- if ($this->_isrollbackonly) {
- throw connectionexception::commitfailedrollbackonly ();
- }
- if ($this->_transactionnestinglevel = = 1) {
- $this->_conn->commit ();
- } Else if ($this->_nesttransactionswithsavepoints) {
- $this->releasesavepoint ($this->_getnestedtransactionsavepointname ());
- }
- --$this->_transactionnestinglevel;
- }
Forget it, don't bother explaining the paragraph:)
Third, Laravel solution Laravel is relatively simple and rough, let's take a look at the operation of creating a transaction:
[PHP]View Plaincopy
- 1.
- /**
- * Author http://www.lai18.com
- * Date 2015-04-19
- * Version 1
- **/
- Public function begintransaction ()
- {
- + +$this->transactions;
- if ($this->transactions = = 1)
- {
- $this->pdo->begintransaction ();
- }
- }
How do you feel? So easy, huh? Judge the current there are several transactions, if it is the first, OK, the start of the transaction, or do nothing, then why do not do anything? Continue to look at the operation of Rollback:
[PHP]View Plaincopy
- 1. /**
- * Author http://www.lai18.com
- * Date 2015-04-19
- * Version 1
- **/
- Public function rollBack ()
- {
- if ($this->transactions = = 1)
- {
- $this->transactions = 0;
- $this->pdo->rollback ();
- }
- Else
- {
- --$this->transactions;
- }
- }
Do you understand? Only if the current transaction is only one time will be true rollback, otherwise just will be counted to do minus one operation. This is why just said Laravel's handling is relatively simple and rough, in the nested inner layer is actually the real business of wood, only the outermost of a whole transaction, although simple and rough, but also solved in the inner layer of a new transaction will cause a commit problem. The principle is this way, in order to maintain integrity, the code of the commit to copy it!
[PHP]View Plaincopy
- Public function commit ()
- {
- if ($this->transactions = = 1) $this->pdo->commit ();
- --$this->transactions;
- }
Two solutions for implementing MySQL nested transactions in PHP