Yii2中事務(wù)的使用實(shí)例代碼詳解
前言
一般我們做業(yè)務(wù)邏輯,都不會(huì)僅僅關(guān)聯(lián)一個(gè)數(shù)據(jù)表,所以,會(huì)面臨事務(wù)問(wèn)題。
數(shù)據(jù)庫(kù)事務(wù)(Database Transaction) ,是指作為單個(gè)邏輯工作單元執(zhí)行的一系列操作,要么完全地執(zhí)行,要么完全地不執(zhí)行。 事務(wù)處理可以確保除非事務(wù)性單元內(nèi)的所有操作都成功完成,否則不會(huì)永久更新面向數(shù)據(jù)的資源。通過(guò)將一組相關(guān)操作組合為一個(gè)要么全部成功要么全部失敗的單元,可以簡(jiǎn)化錯(cuò)誤恢復(fù)并使應(yīng)用程序更加可靠。一個(gè)邏輯工作單元要成為事務(wù),必須滿足所謂的ACID(原子性、一致性、隔離性和持久性)屬性。事務(wù)是數(shù)據(jù)庫(kù)運(yùn)行中的一個(gè)邏輯工作單位,由DBMS中的事務(wù)管理子系統(tǒng)負(fù)責(zé)事務(wù)的處理。
準(zhǔn)備
數(shù)據(jù)庫(kù)引擎為innodb
本文使用的yii版本為2.0.5,只要是2.0以上就沒(méi)有問(wèn)題
運(yùn)行環(huán)境為PHP7.0.0,Mysql5.6
Yii中的事務(wù)
處理異常
/** * 測(cè)試事務(wù) */ public function actionTest(){ //創(chuàng)建事務(wù) $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; //寫(xiě)入不存在的字段 if(!$test->save()){ "save fail"; //如果沒(méi)有寫(xiě)入就輸出 } //提交 $tr->commit(); } catch (Exception $e) { //回滾 $tr->rollBack(); echo "rollback"; } }
運(yùn)行結(jié)果
save 1 | save 2 | save 3 | rollback
注意,因?yàn)樽詈蟮臄?shù)據(jù)沒(méi)有插入成功,觸發(fā)了事務(wù)的回滾,所以數(shù)據(jù)表沒(méi)有新增數(shù)據(jù)產(chǎn)生。
觸發(fā)事務(wù)回滾的原因是代碼出現(xiàn)了異常(Exception)。
處理數(shù)據(jù)失敗
一般來(lái)講,我們的運(yùn)行中的代碼是不會(huì)出現(xiàn)這種明顯的異常,這種異常會(huì)在開(kāi)發(fā)測(cè)試過(guò)程中消滅掉。
而真正造成數(shù)據(jù)需要回滾的是我們的某個(gè)業(yè)務(wù)出現(xiàn)問(wèn)題,導(dǎo)致沒(méi)有寫(xiě)入部分的數(shù)據(jù)。
/** * 測(cè)試事務(wù) */ public function actionTest(){ //創(chuàng)建事務(wù) $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; //數(shù)據(jù)庫(kù)設(shè)計(jì)name不能為空,人為造成寫(xiě)入失敗。 $test->sort=1; //寫(xiě)入不存在的字段 if(!$test->save()){ echo "save fail"; //如果沒(méi)有寫(xiě)入就輸出 } //提交 $tr->commit(); } catch (Exception $e) { //回滾 $tr->rollBack(); echo "rollback"; } }
運(yùn)行結(jié)果如下,數(shù)據(jù)庫(kù)插入了三條數(shù)據(jù)。
save 1 | save 2 | save 3 | save fail
也就是說(shuō),如果因?yàn)闃I(yè)務(wù)邏輯導(dǎo)致某個(gè)數(shù)據(jù)表沒(méi)有寫(xiě)入數(shù)據(jù),也沒(méi)有出現(xiàn)對(duì)應(yīng)的回滾。
改進(jìn)方案如下
/** * 測(cè)試事務(wù) */ public function actionTest(){ //創(chuàng)建事務(wù) $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; //數(shù)據(jù)庫(kù)設(shè)計(jì)name不能為空,人為造成寫(xiě)入失敗。 $test->sort=1; //寫(xiě)入不存在的字段 if(!$test->save()){ throw new \yii\db\Exception(); //手動(dòng)拋出異常,再由下面捕獲。 } //提交 $tr->commit(); } catch (Exception $e) { //回滾 $tr->rollBack(); echo "rollback"; } }
運(yùn)行結(jié)果如下,數(shù)據(jù)庫(kù)沒(méi)有插入新數(shù)據(jù),事務(wù)被回滾。
save 1 | save 2 | save 3 | rollback
分散的數(shù)據(jù)處理
由于實(shí)際項(xiàng)目的復(fù)雜程度,導(dǎo)致我們的數(shù)據(jù)庫(kù)操作分散在不同的Model中。
所以,實(shí)際項(xiàng)目的代碼不會(huì)是上面的樣子。
模擬需求
接收參數(shù):
名字
性別
簽名
業(yè)務(wù)處理流程:
接收參數(shù)
由發(fā)號(hào)器得到用戶的uid,發(fā)號(hào)器對(duì)應(yīng)數(shù)據(jù)表增加一位數(shù)字
把名字、性別、簽名和上一步的uid寫(xiě)入用戶信息表
初始化用戶余額表
回滾觸發(fā)時(shí)機(jī):
初始化余額表沒(méi)有傳入uid導(dǎo)出沒(méi)有寫(xiě)入數(shù)據(jù)
實(shí)際代碼
//Controller /** * 測(cè)試事務(wù)-注冊(cè)用戶 */ public function actionReg() { //獲取請(qǐng)求 $request = Yii::$app->request; //設(shè)定返回格式 $response = Yii::$app->response; $response->format = \yii\web\Response::FORMAT_JSON; //返回json //測(cè)試代碼,去掉驗(yàn)證身份步驟 $name = $request->get("name"); $gender = $request->get("gender"); $sign = $request->get("sign"); //測(cè)試代碼,省略參數(shù)校驗(yàn)步驟 $tr = Yii::$app->db->beginTransaction(); try { //得到uid $uid = App::getSeNo(); UserProfile::add($uid, $name, $gender, 1, $sign); $user_balance = UserBalance::initUserBalance($uid); $tr->commit(); //提交數(shù)據(jù) } 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("沒(méi)有寫(xiě)入用戶資料"); } return $model; } //UserBalance /** * 初始化用戶的可提現(xiàn)余額 * @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("沒(méi)有初始化用戶余額"); } $info=$model; } return $info->attributes; }
正常的結(jié)果如下
{"id":124,"user_id":1473179883,"price":"0","update_time":1473179883}
如果把初始化用戶余額部分的user_id沒(méi)有傳遞成功,返回的結(jié)果如下
"沒(méi)有初始化用戶余額"
我們可以針對(duì)具體情況定位到錯(cuò)誤所在位置,及時(shí)修改。
事務(wù)(Transaction)
從上面的實(shí)際代碼可以看出,創(chuàng)建了事務(wù),只要在范圍內(nèi),就算是引入的別的Model也能把異常NG返回,完成回滾操作。
一般情況下,整個(gè)Yii應(yīng)用使用了同一個(gè)數(shù)據(jù)庫(kù)連接,或者說(shuō)是使用了單例。
而在yii\db\Connection中,又對(duì)事務(wù)對(duì)象進(jìn)行了緩存:
class Connection extends Component { // 保存當(dāng)前連接的有效Transaction對(duì)象 private $_transaction; // 已經(jīng)緩存有事務(wù)對(duì)象,且事務(wù)對(duì)象有效,則返回該事務(wù)對(duì)象 // 否則返回null public function getTransaction() { return $this->_transaction && $this->_transaction->getIsActive() ? $this->_transaction : null; } // 看看啟用事務(wù)時(shí),是如何使用事務(wù)對(duì)象的 public function beginTransaction($isolationLevel = null) { $this->open(); // 緩存的事務(wù)對(duì)象有效,則使用緩存中的事務(wù)對(duì)象 // 否則創(chuàng)建一個(gè)新的事務(wù)對(duì)象 if (($transaction = $this->getTransaction()) === null) { $transaction = $this->_transaction = new Transaction(['db' => $this]); } $transaction->begin($isolationLevel); return $transaction; } }
因此,可以認(rèn)為整個(gè)Yii應(yīng)用,使用了同一個(gè) Transaction 對(duì)象,也就是說(shuō), Transaction::_level 在整個(gè)應(yīng)用的生命周期中,是有延續(xù)性的。 這是實(shí)現(xiàn)事務(wù)嵌套的關(guān)鍵和前提。
以上所述是小編給大家介紹的Yii2中事務(wù)的使用實(shí)例代碼詳解,希望對(duì)大家有所幫助,如果大家有任何疑問(wèn)請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!
相關(guān)文章
php兩個(gè)多維數(shù)組組合遍歷的實(shí)例
下面小編就為大家?guī)?lái)一篇php兩個(gè)多維數(shù)組組合遍歷的實(shí)例。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-10-10thinkPHP自動(dòng)驗(yàn)證機(jī)制詳解
這篇文章主要介紹了thinkPHP自動(dòng)驗(yàn)證機(jī)制,結(jié)合實(shí)例形式分析了thinkPHP自動(dòng)驗(yàn)證機(jī)制的格式、原理與具體操作技巧,需要的朋友可以參考下2016-12-12使用PHP實(shí)現(xiàn)阻止用戶上傳成人照片或者裸照
這篇文章主要介紹了使用PHP實(shí)現(xiàn)阻止用戶上傳成人照片或者裸照,方法和思路都非常的不錯(cuò),推薦給大家,需要的朋友可以參考下2014-12-12Laravel 5.4因特殊字段太長(zhǎng)導(dǎo)致migrations報(bào)錯(cuò)的解決
這篇文章主要給大家介紹了關(guān)于Laravel 5.4因特殊字段太長(zhǎng)導(dǎo)致migrations報(bào)錯(cuò)的解決方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起看看吧。2017-10-10微信公眾平臺(tái)網(wǎng)頁(yè)授權(quán)獲取用戶基本信息中授權(quán)回調(diào)域名設(shè)置的變動(dòng)
這篇文章主要介紹了微信公眾平臺(tái)網(wǎng)頁(yè)授權(quán)獲取用戶基本信息中授權(quán)回調(diào)域名設(shè)置的變動(dòng),需要的朋友可以參考下2014-10-10詳解PHP使用日期時(shí)間處理器Carbon人性化顯示時(shí)間
本篇文章主要介紹了詳解PHP使用日期時(shí)間處理器Carbon人性化顯示時(shí)間,具有一定的參考價(jià)值,有興趣的可以了解一下2017-08-08