1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\db;
use Yii;
use yii\base\InvalidConfigException;
/**
* Transaction represents a DB transaction.
*
* It is usually created by calling [[Connection::beginTransaction()]].
*
* The following code is a typical example of using transactions (note that some
* DBMS may not support transactions):
*
* ~~~
* $transaction = $connection->beginTransaction();
* try {
* $connection->createCommand($sql1)->execute();
* $connection->createCommand($sql2)->execute();
* //.... other SQL executions
* $transaction->commit();
* } catch (Exception $e) {
* $transaction->rollBack();
* }
* ~~~
*
* @property boolean $isActive Whether this transaction is active. Only an active transaction can [[commit()]]
* or [[rollBack()]]. This property is read-only.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class Transaction extends \yii\base\Object
{
/**
* @var Connection the database connection that this transaction is associated with.
*/
public $db;
/**
* @var integer the nesting level of the transaction. 0 means the outermost level.
*/
private $_level = 0;
/**
* Returns a value indicating whether this transaction is active.
* @return boolean whether this transaction is active. Only an active transaction
* can [[commit()]] or [[rollBack()]].
*/
public function getIsActive()
{
return $this->_level > 0 && $this->db && $this->db->isActive;
}
/**
* Begins a transaction.
* @throws InvalidConfigException if [[db]] is `null`.
*/
public function begin()
{
if ($this->db === null) {
throw new InvalidConfigException('Transaction::db must be set.');
}
$this->db->open();
if ($this->_level == 0) {
Yii::trace('Begin transaction', __METHOD__);
$this->db->pdo->beginTransaction();
$this->_level = 1;
return;
}
$schema = $this->db->getSchema();
if ($schema->supportsSavepoint()) {
Yii::trace('Set savepoint ' . $this->_level, __METHOD__);
$schema->createSavepoint('LEVEL' . $this->_level);
} else {
Yii::info('Transaction not started: nested transaction not supported', __METHOD__);
}
$this->_level++;
}
/**
* Commits a transaction.
* @throws Exception if the transaction is not active
*/
public function commit()
{
if (!$this->getIsActive()) {
throw new Exception('Failed to commit transaction: transaction was inactive.');
}
$this->_level--;
if ($this->_level == 0) {
Yii::trace('Commit transaction', __METHOD__);
$this->db->pdo->commit();
return;
}
$schema = $this->db->getSchema();
if ($schema->supportsSavepoint()) {
Yii::trace('Release savepoint ' . $this->_level, __METHOD__);
$schema->releaseSavepoint('LEVEL' . $this->_level);
} else {
Yii::info('Transaction not committed: nested transaction not supported', __METHOD__);
}
}
/**
* Rolls back a transaction.
* @throws Exception if the transaction is not active
*/
public function rollBack()
{
if (!$this->getIsActive()) {
throw new Exception('Failed to roll back transaction: transaction was inactive.');
}
$this->_level--;
if ($this->_level == 0) {
Yii::trace('Roll back transaction', __METHOD__);
$this->db->pdo->rollBack();
return;
}
$schema = $this->db->getSchema();
if ($schema->supportsSavepoint()) {
Yii::trace('Roll back to savepoint ' . $this->_level, __METHOD__);
$schema->rollBackSavepoint('LEVEL' . $this->_level);
} else {
Yii::info('Transaction not rolled back: nested transaction not supported', __METHOD__);
// throw an exception to fail the outer transaction
throw new Exception('Roll back failed: nested transaction not supported.');
}
}
}