Transaction.php 3.72 KB
Newer Older
w  
Qiang Xue committed
1 2 3
<?php
/**
 * @link http://www.yiiframework.com/
Qiang Xue committed
4
 * @copyright Copyright (c) 2008 Yii Software LLC
w  
Qiang Xue committed
5 6 7
 * @license http://www.yiiframework.com/license/
 */

Qiang Xue committed
8
namespace yii\db;
w  
Qiang Xue committed
9

10
use Yii;
Qiang Xue committed
11
use yii\base\InvalidConfigException;
w  
Qiang Xue committed
12

w  
Qiang Xue committed
13
/**
w  
Qiang Xue committed
14 15
 * Transaction represents a DB transaction.
 *
Qiang Xue committed
16
 * It is usually created by calling [[Connection::beginTransaction()]].
w  
Qiang Xue committed
17
 *
w  
Qiang Xue committed
18 19
 * The following code is a typical example of using transactions (note that some
 * DBMS may not support transactions):
w  
Qiang Xue committed
20
 *
w  
Qiang Xue committed
21 22 23
 * ~~~
 * $transaction = $connection->beginTransaction();
 * try {
24 25 26 27
 *     $connection->createCommand($sql1)->execute();
 *     $connection->createCommand($sql2)->execute();
 *     //.... other SQL executions
 *     $transaction->commit();
Qiang Xue committed
28
 * } catch(Exception $e) {
29
 *     $transaction->rollBack();
w  
Qiang Xue committed
30
 * }
w  
Qiang Xue committed
31
 * ~~~
w  
Qiang Xue committed
32
 *
33
 * @property boolean $isActive Whether this transaction is active. Only an active transaction can [[commit()]]
34
 * or [[rollBack()]]. This property is read-only.
35
 *
w  
Qiang Xue committed
36
 * @author Qiang Xue <qiang.xue@gmail.com>
w  
Qiang Xue committed
37
 * @since 2.0
w  
Qiang Xue committed
38
 */
Qiang Xue committed
39
class Transaction extends \yii\base\Object
w  
Qiang Xue committed
40
{
41 42 43
	/**
	 * @var Connection the database connection that this transaction is associated with.
	 */
Qiang Xue committed
44
	public $db;
w  
Qiang Xue committed
45
	/**
46
	 * @var integer the nesting level of the transaction. 0 means the outermost level.
w  
Qiang Xue committed
47
	 */
48
	private $_level = 0;
49

w  
Qiang Xue committed
50
	/**
51 52
	 * Returns a value indicating whether this transaction is active.
	 * @return boolean whether this transaction is active. Only an active transaction
53
	 * can [[commit()]] or [[rollBack()]].
w  
Qiang Xue committed
54
	 */
55 56
	public function getIsActive()
	{
57
		return $this->_level > 0 && $this->db && $this->db->isActive;
58
	}
w  
Qiang Xue committed
59 60

	/**
61
	 * Begins a transaction.
62
	 * @throws InvalidConfigException if [[db]] is `null`.
w  
Qiang Xue committed
63
	 */
64
	public function begin()
w  
Qiang Xue committed
65
	{
66 67 68 69 70 71 72
		if ($this->db === null) {
			throw new InvalidConfigException('Transaction::db must be set.');
		}
		$this->db->open();

		if ($this->_level == 0) {
			Yii::trace('Begin transaction', __METHOD__);
Qiang Xue committed
73
			$this->db->pdo->beginTransaction();
74 75 76 77 78 79 80 81 82 83
			$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__);
84
		}
85
		$this->_level++;
w  
Qiang Xue committed
86 87 88 89
	}

	/**
	 * Commits a transaction.
90
	 * @throws Exception if the transaction is not active
w  
Qiang Xue committed
91 92 93
	 */
	public function commit()
	{
94 95 96 97 98 99 100
		if (!$this->getIsActive()) {
			throw new Exception('Failed to commit transaction: transaction was inactive.');
		}

		$this->_level--;
		if ($this->_level == 0) {
			Yii::trace('Commit transaction', __METHOD__);
Qiang Xue committed
101
			$this->db->pdo->commit();
102 103 104 105 106 107 108
			return;
		}

		$schema = $this->db->getSchema();
		if ($schema->supportsSavepoint()) {
			Yii::trace('Release savepoint ' . $this->_level, __METHOD__);
			$schema->releaseSavepoint('LEVEL' . $this->_level);
Qiang Xue committed
109
		} else {
110
			Yii::info('Transaction not committed: nested transaction not supported', __METHOD__);
w  
Qiang Xue committed
111 112 113 114 115
		}
	}

	/**
	 * Rolls back a transaction.
116
	 * @throws Exception if the transaction is not active
w  
Qiang Xue committed
117
	 */
118
	public function rollBack()
w  
Qiang Xue committed
119
	{
120 121 122 123 124 125 126
		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__);
127
			$this->db->pdo->rollBack();
128 129 130 131 132 133 134
			return;
		}

		$schema = $this->db->getSchema();
		if ($schema->supportsSavepoint()) {
			Yii::trace('Roll back to savepoint ' . $this->_level, __METHOD__);
			$schema->rollBackSavepoint('LEVEL' . $this->_level);
Qiang Xue committed
135
		} else {
136 137 138
			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.');
w  
Qiang Xue committed
139 140 141
		}
	}
}