Yii2中事務的使用實例代碼詳解
前言
一般我們做業(yè)務邏輯,都不會僅僅關聯一個數據表,所以,會面臨事務問題。
數據庫事務(Database Transaction) ,是指作為單個邏輯工作單元執(zhí)行的一系列操作,要么完全地執(zhí)行,要么完全地不執(zhí)行。 事務處理可以確保除非事務性單元內的所有操作都成功完成,否則不會永久更新面向數據的資源。通過將一組相關操作組合為一個要么全部成功要么全部失敗的單元,可以簡化錯誤恢復并使應用程序更加可靠。一個邏輯工作單元要成為事務,必須滿足所謂的ACID(原子性、一致性、隔離性和持久性)屬性。事務是數據庫運行中的一個邏輯工作單位,由DBMS中的事務管理子系統(tǒng)負責事務的處理。
準備
數據庫引擎為innodb
本文使用的yii版本為2.0.5,只要是2.0以上就沒有問題
運行環(huán)境為PHP7.0.0,Mysql5.6
Yii中的事務
處理異常
/** * 測試事務 */ public function actionTest(){ //創(chuàng)建事務 $tr = Yii::$app->db->beginTransaction(); try { for($i=1;$i<=3;$i++){ $test = new Areas(); $test->name = 'name'.$i; $test->sort=1; if($test->save()){ echo "save $i | "; } } $test = new Areas(); $test->name = 'ab'.$i; $test->sorta=1; //寫入不存在的字段 if(!$test->save()){ "save fail"; //如果沒有寫入就輸出 } //提交 $tr->commit(); } catch (Exception $e) { //回滾 $tr->rollBack(); echo "rollback"; } }
運行結果
save 1 | save 2 | save 3 | rollback
注意,因為最后的數據沒有插入成功,觸發(fā)了事務的回滾,所以數據表沒有新增數據產生。
觸發(fā)事務回滾的原因是代碼出現了異常(Exception)。
處理數據失敗
一般來講,我們的運行中的代碼是不會出現這種明顯的異常,這種異常會在開發(fā)測試過程中消滅掉。
而真正造成數據需要回滾的是我們的某個業(yè)務出現問題,導致沒有寫入部分的數據。
/** * 測試事務 */ public function actionTest(){ //創(chuàng)建事務 $tr = Yii::$app->db->beginTransaction(); try { for($i=1;$i<=3;$i++){ $test = new Areas(); $test->name = 'name'.$i; $test->sort=1; if($test->save()){ echo "save $i | "; } } $test = new Areas(); $test->name = null; //數據庫設計name不能為空,人為造成寫入失敗。 $test->sort=1; //寫入不存在的字段 if(!$test->save()){ echo "save fail"; //如果沒有寫入就輸出 } //提交 $tr->commit(); } catch (Exception $e) { //回滾 $tr->rollBack(); echo "rollback"; } }
運行結果如下,數據庫插入了三條數據。
save 1 | save 2 | save 3 | save fail
也就是說,如果因為業(yè)務邏輯導致某個數據表沒有寫入數據,也沒有出現對應的回滾。
改進方案如下
/** * 測試事務 */ public function actionTest(){ //創(chuàng)建事務 $tr = Yii::$app->db->beginTransaction(); try { for($i=1;$i<=3;$i++){ $test = new Areas(); $test->name = 'name'.$i; $test->sort=1; if($test->save()){ echo "save $i | "; } } $test = new Areas(); $test->name = null; //數據庫設計name不能為空,人為造成寫入失敗。 $test->sort=1; //寫入不存在的字段 if(!$test->save()){ throw new \yii\db\Exception(); //手動拋出異常,再由下面捕獲。 } //提交 $tr->commit(); } catch (Exception $e) { //回滾 $tr->rollBack(); echo "rollback"; } }
運行結果如下,數據庫沒有插入新數據,事務被回滾。
save 1 | save 2 | save 3 | rollback
分散的數據處理
由于實際項目的復雜程度,導致我們的數據庫操作分散在不同的Model中。
所以,實際項目的代碼不會是上面的樣子。
模擬需求
接收參數:
名字
性別
簽名
業(yè)務處理流程:
接收參數
由發(fā)號器得到用戶的uid,發(fā)號器對應數據表增加一位數字
把名字、性別、簽名和上一步的uid寫入用戶信息表
初始化用戶余額表
回滾觸發(fā)時機:
初始化余額表沒有傳入uid導出沒有寫入數據
實際代碼
//Controller /** * 測試事務-注冊用戶 */ public function actionReg() { //獲取請求 $request = Yii::$app->request; //設定返回格式 $response = Yii::$app->response; $response->format = \yii\web\Response::FORMAT_JSON; //返回json //測試代碼,去掉驗證身份步驟 $name = $request->get("name"); $gender = $request->get("gender"); $sign = $request->get("sign"); //測試代碼,省略參數校驗步驟 $tr = Yii::$app->db->beginTransaction(); try { //得到uid $uid = App::getSeNo(); UserProfile::add($uid, $name, $gender, 1, $sign); $user_balance = UserBalance::initUserBalance($uid); $tr->commit(); //提交數據 } catch (Exception $e) { //回滾 $tr->rollBack(); return $e->getMessage(); //返回自定義異常信息 } return $user_balance; } //UserProfile /** * 添加用戶信息 * @param $user_id * @param $nikename * @param $gender * @param $user_type * @param string $intro * @return UserProfile * @throws \Exception */ public static function add($user_id, $nikename, $gender,$user_type,$intro="") { $model = new UserProfile(); $model->gender = $gender; $model->nikename = $nikename; $model->user_id = $user_id; $model->user_type=$user_type; $model->intro=$intro; $model->update_time = time(); $insert =$model->insert(); if(!$insert){ throw new Exception("沒有寫入用戶資料"); } return $model; } //UserBalance /** * 初始化用戶的可提現余額 * @param $user_id */ public static function initUserBalance($user_id){ $info=self::find()->where(['user_id'=>$user_id])->one(); if(!$info ){ $model=new UserBalance(); $model->user_id = $user_id; $model->price= "0"; $model->update_time=time(); $insert = $model->insert(); if(!$insert){ throw new Exception("沒有初始化用戶余額"); } $info=$model; } return $info->attributes; }
正常的結果如下
{"id":124,"user_id":1473179883,"price":"0","update_time":1473179883}
如果把初始化用戶余額部分的user_id沒有傳遞成功,返回的結果如下
"沒有初始化用戶余額"
我們可以針對具體情況定位到錯誤所在位置,及時修改。
事務(Transaction)
從上面的實際代碼可以看出,創(chuàng)建了事務,只要在范圍內,就算是引入的別的Model也能把異常NG返回,完成回滾操作。
一般情況下,整個Yii應用使用了同一個數據庫連接,或者說是使用了單例。
而在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(); // 緩存的事務對象有效,則使用緩存中的事務對象 // 否則創(chuàng)建一個新的事務對象 if (($transaction = $this->getTransaction()) === null) { $transaction = $this->_transaction = new Transaction(['db' => $this]); } $transaction->begin($isolationLevel); return $transaction; } }
因此,可以認為整個Yii應用,使用了同一個 Transaction 對象,也就是說, Transaction::_level 在整個應用的生命周期中,是有延續(xù)性的。 這是實現事務嵌套的關鍵和前提。
以上所述是小編給大家介紹的Yii2中事務的使用實例代碼詳解,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對腳本之家網站的支持!
相關文章
Laravel 5.4因特殊字段太長導致migrations報錯的解決
這篇文章主要給大家介紹了關于Laravel 5.4因特殊字段太長導致migrations報錯的解決方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起看看吧。2017-10-10微信公眾平臺網頁授權獲取用戶基本信息中授權回調域名設置的變動
這篇文章主要介紹了微信公眾平臺網頁授權獲取用戶基本信息中授權回調域名設置的變動,需要的朋友可以參考下2014-10-10讓CodeIgniter的ellipsize()支持中文截斷的方法
CodeIgniter的Text Helper有一個ellipsize()方法,用來過濾HTML標簽并且截斷文字十分好用。但是它對中文支持的特別不好,在中文中使用就有亂碼出現。這篇文章主要介紹了讓CodeIgniter的ellipsize()支持中文截斷的方法,需要的朋友可以參考下2014-06-06