<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                在Yii中,使用?yii\db\Transaction?來表示數據庫事務。 一般情況下,我們從數據庫連接啟用事務,通常采用如下的形式: ~~~ $transaction = $connection->beginTransaction(); try { $connection->createCommand($sql1)->execute(); $connection->createCommand($sql2)->execute(); // ... executing other SQL statements ... $transaction->commit(); } catch (Exception $e) { $transaction->rollBack(); } ~~~ 在上面的代碼中,先是獲取一個?yii\db\Transaction?對象,之后執行若干SQL 語句,然后調用之前Transaction?對象的?commit()?方法。這一過程中, 如果捕獲了異常,那么調用?rollBack()?進行回滾。 ## 創建事務[](http://www.digpage.com/transaction.html#id2 "Permalink to this headline") 在上面代碼中,我們使用數據庫連接的?beginTransaction()?方法, 創建了一個?yii\db\Trnasaction對象,具體代碼在?yii\db\Connection?中: ~~~ public function beginTransaction($isolationLevel = null) { $this->open(); // 尚未初始化當前連接使用的Transaction對象,則創建一個 if (($transaction = $this->getTransaction()) === null) { $transaction = $this->_transaction = new Transaction(['db' => $this]); } // 獲取Transaction后,就可以啟用事務 $transaction->begin($isolationLevel); return $transaction; } ~~~ 從創建?Transaction?對象的?new?Transaction(['db'?=>?$this])?形式來看, 這也是Yii一貫的風格。這里簡單的初始化了?yii\db\Transaction::db?。 這表示的是當前的?Transaction?所依賴的數據庫連接。如果未對其進行初始化, 那么將無法正常使用事務。 在獲取了?Transaction?之后,就可以調用他的?begin()?方法,來啟用事務。 必要的情況下,還可以指定事務隔離級別。 事務隔離級別的設定,由?yii\db\Schema::setTransactionIsolationLevel()?方法來實現,而這個方法,無非就是執行了如下的SQL語句: ~~~ SET TRANSACTION ISOLATION LEVEL ... ~~~ 對于隔離級別,yii\db\Transaction?也提前定義了幾個常量: ~~~ const READ_UNCOMMITTED = 'READ UNCOMMITTED'; const READ_COMMITTED = 'READ COMMITTED'; const REPEATABLE_READ = 'REPEATABLE READ'; const SERIALIZABLE = 'SERIALIZABLE'; ~~~ 如果開發者沒有給出隔離級別,那么,數據庫會使用默認配置的隔離級別。 比如,對于MySQL而言,就是使用?transaction-isolation?配置項的值。 ## 啟用事務[](http://www.digpage.com/transaction.html#id3 "Permalink to this headline") 上面的代碼告訴我們,啟用事務,最終是靠調用?Transaction::begin()?來實現的。 那么就讓我們來看看他的代碼吧: ~~~ public function begin($isolationLevel = null) { // 沒有初始化數據庫連接的滾粗 if ($this->db === null) { throw new InvalidConfigException('Transaction::db must be set.'); } $this->db->open(); // _level 為0 表示的是最外層的事務 if ($this->_level == 0) { // 如果給定了隔離級別,那么就設定之 if ($isolationLevel !== null) { // 設定事務隔離級別 $this->db->getSchema()->setTransactionIsolationLevel($isolationLevel); } Yii::trace('Begin transaction' . ($isolationLevel ? ' with isolation level ' . $isolationLevel : ''), __METHOD__); $this->db->trigger(Connection::EVENT_BEGIN_TRANSACTION); $this->db->pdo->beginTransaction(); $this->_level = 1; return; } // 以下 _level>0 表示的是嵌套的事務 $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__); } // 結合 _level == 0 分支中的 $this->_level = 1, // 可以得知,一旦調用這個方法, _level 就會自增1 $this->_level++; } ~~~ 對于最外層的事務,即當?_level?為 0 時,最終落到PDO的?beginTransaction()?來啟用事務。在啟用前,如果開發者給定了隔離級別,那么還需要設定隔離級別。 當?_level?>?0?時,表示的是嵌套的事務,并非最外層的事務。 對此,Yii使用 SQL 的?SAVEPOINT?和ROLLBACK?TO?SAVEPOINT?來實現設置事務保存點和回滾到保存點的操作。 ## 嵌套事務[](http://www.digpage.com/transaction.html#id4 "Permalink to this headline") 在開頭的例子中,展現的是事務最簡單的使用形式。Yii還允許把事務嵌套起來使用。 比如,可以采用如下形式來使用事務: ~~~ $outerTransaction = $db->beginTransaction(); try { $db->createCommand($sql1)->execute(); $innerTransaction = $db->beginTransaction(); try { $db->createCommand($sql2)->execute(); $db->createCommand($sql3)->execute(); $innerTransaction->commit(); } catch (Exception $e) { $innerTransaction->rollBack(); } $db->createCommand($sql4)->execute(); $outerTransaction->commit(); } catch (Exception $e) { $outerTransaction->rollBack(); } ~~~ 為了實現這一嵌套,Yii使用?yii\db\Transaction::_level?來表示嵌套的層級。 當層級為 0 時,表示的是最外層的事務。 一般情況下,整個Yii應用使用了同一個數據庫連接,或者說是使用了單例。 具體可以看?[_服務定位器(Service Locator)_](http://www.digpage.com/service_locator.html#service-locator)?部分。 而在?yii\db\Connection?中,又對事務對象進行了緩存: ~~~ class Connection extends Component { // 保存當前連接的有效Transaction對象 private $_transaction; // 已經緩存有事務對象,且事務對象有效,則返回該事務對象 // 否則返回null public function getTransaction() { return $this->_transaction && $this->_transaction->getIsActive() ? $this->_transaction : null; } // 看看啟用事務時,是如何使用事務對象的 public function beginTransaction($isolationLevel = null) { $this->open(); // 緩存的事務對象有效,則使用緩存中的事務對象 // 否則創建一個新的事務對象 if (($transaction = $this->getTransaction()) === null) { $transaction = $this->_transaction = new Transaction(['db' => $this]); } $transaction->begin($isolationLevel); return $transaction; } } ~~~ 因此,可以認為整個Yii應用,使用了同一個?Transaction?對象,也就是說,?Transaction::_level?在整個應用的生命周期中,是有延續性的。 這是實現事務嵌套的關鍵和前提。 在這個?Transaction::_level?的基礎上,Yii實現了事務的嵌套: * 事務對象初始化時,設?_level?為0,表示如果要啟用事務, 這是一個最外層的事務。 * 每當調用?Transaction::begin()?來啟用具體事務時,?_level?自增1。 表示如再啟用事務,將是層級為1的嵌套事務。 * 每當調用?Transaction::commit()?或?Transaction::rollBack()?時,?_level?自減1,表示當前層級的事務處理完畢,返回上一層級的事務中。 * 當調用了一次?begin()?且還沒有調用匹配的?commit()?或?rollBack()?, 就再次調用?begin()時,會使事務進行更深一層級的嵌套中。 因此,就有了我們上面代碼中,當?_level?為 0 時,需要設定事務隔離級別。 因為這是最外層事務。 而當?_level?>?0?時,由于是“嵌套”的事務,一個大事務中的小“事務”,那么, 就使用保存點及其回滾、釋放操作,來模擬事務的啟用、回滾和提交操作。 要注意,在這一節的開頭,我們使用2對嵌套的?try?...?catch?來實現事務的嵌套。 由于內層的catch?把可能拋出的異常吞了,不再繼續拋出。那么, 外層的?catch?,是捕獲不到內層的異常的。 也就是說,這種情況下,外層中的?$sql1?$sql4?不會由于?$sql2?或?$sql3?的失敗而中止,?$sql1$sql4?可以繼續執行并?commit?。 這是嵌套事務的正確使用形式,即內外層之間應當是不相干的。 如果內層事務的異常,會導致外層事務需要回滾,那么我們不應該使用事務嵌套, 而是應該把內外層當成一個事務。這個道理很淺顯,但是事實開發中,一個不小心, 就會出昏招。所以,不要動不動就來個?beginTransaction()?。 當然,為了使代碼功能有一定的層次感,在必要時,也可以使用嵌套的事務。 但要考慮好,子事務是否真的要吞掉異常?有沒有必要繼續拋出異常, 使得上一層級的事務也產生回滾?這個要根據實際的情形來確定。 ## 提交和回滾[](http://www.digpage.com/transaction.html#id5 "Permalink to this headline") 提交和回滾通過?Transaction::commit()?和?Transaction::rollBack()?來實現: ~~~ public function commit() { if (!$this->getIsActive()) { throw new Exception('Failed to commit transaction: transaction was inactive.'); } // 與begin()對應,只要調用 commit(),_level 自減1 $this->_level--; // 如果回到了最外層事務,那么應當使用PDO的commit if ($this->_level == 0) { Yii::trace('Commit transaction', __METHOD__); $this->db->pdo->commit(); $this->db->trigger(Connection::EVENT_COMMIT_TRANSACTION); 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__); } } public function rollBack() { if (!$this->getIsActive()) { return; } // 調用 rollBack() 也會使 _level 自減1 $this->_level--; // 如果已經返回到最外層,那么調用 PDO 的 rollBack if ($this->_level == 0) { Yii::trace('Roll back transaction', __METHOD__); $this->db->pdo->rollBack(); $this->db->trigger(Connection::EVENT_ROLLBACK_TRANSACTION); 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 new Exception('Roll back failed: nested transaction not supported.'); } } ~~~ 對于提交和回滾: * 提交時,會使層級+1,回滾時,會使層級-1 * 對于最外層的提交和回滾,使用的是數據庫事務的?commit?和?rollBack * 對于嵌套的內層的提交和回滾,使用的其實是事務保存點的釋放和回滾 * 釋放保存點時,會釋放保存點的標識符,這個標識符在下次事務嵌套達到這個層級時, 會被再次使用。 ## 有效的事務[](http://www.digpage.com/transaction.html#id6 "Permalink to this headline") 在上面的提交、回滾等方法的代碼中,我們多次看到了一個?this->getIsActive()?。 這是用于判斷當前事務是否有效的一個方法,我們通過它,來看看什么樣的一個事務, 算是有效的: ~~~ public function getIsActive() { return $this->_level > 0 && $this->db && $this->db->isActive; } ~~~ 方法很簡單明了,一個有效的事務必須同時滿足3個條件: * _level?>?0?。這是由于為0是,要么是剛剛初始化, 要么是所有的事務已經提交或回滾了。也就是說,只有調用過了?begin()?但還沒有調用過匹配的?commit()?或?rollBack()?的事務對象,才是有效的。 * 數據庫連接要已經初始化。 * 數據庫連接也必須是有效的。 如果覺得《深入理解Yii2.0》對您有所幫助,也請[幫助《深入理解Yii2.0》](http://www.digpage.com/donate.html#donate)。 謝謝!
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看