【PHP】Yii2中事務的使用以及程式碼例項
Yii2中事務的使用以及程式碼例項
前言
一般我們做業務邏輯,都不會僅僅關聯一個資料表,所以,會面臨事務問題。
資料庫事務(Database Transaction) ,是指作為單個邏輯工作單元執行的一系列操作,要麼完全地執行,要麼完全地不執行。 事務處理可以確保除非事務性單元內的所有操作都成功完成,否則不會永久更新面向資料的資源。通過將一組相關操作組合為一個要麼全部成功要麼全部失敗的單元,可以簡化錯誤恢復並使應用程式更加可靠。一個邏輯工作單元要成為事務,必須滿足所謂的ACID(原子性、一致性、隔離性和永續性)屬性。事務是資料庫執行中的一個邏輯工作單位,由DBMS中的事務管理子系統負責事務的處理。
準備
- 資料庫引擎為innodb
- 本文使用的yii版本為2.0.5,只要是2.0以上就沒有問題
- 執行環境為PHP7.0.0,Mysql5.6
Yii中的事務
處理異常
/**
* 測試事務
*/
public function actionTest(){
//建立事務
$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
注意,因為最後的資料沒有插入成功,觸發了事務的回滾,所以資料表沒有新增資料產生。
觸發事務回滾的原因是程式碼出現了異常(Exception)。
處理資料失敗
一般來講,我們的執行中的程式碼是不會出現這種明顯的異常,這種異常會在開發測試過程中消滅掉。
而真正造成資料需要回滾的是我們的某個業務出現問題,導致沒有寫入部分的資料。
/**
* 測試事務
*/
public function actionTest(){
//建立事務
$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
也就是說,如果因為業務邏輯導致某個資料表沒有寫入資料,也沒有出現對應的回滾。
改進方案如下
/**
* 測試事務
*/
public function actionTest(){
//建立事務
$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 yiidbException(); //手動丟擲異常,再由下面捕獲。
}
//提交
$tr->commit();
} catch (Exception $e) {
//回滾
$tr->rollBack();
echo "rollback";
}
}
執行結果如下,資料庫沒有插入新資料,事務被回滾。
save 1 | save 2 | save 3 | rollback
分散的資料處理
由於實際專案的複雜程度,導致我們的資料庫操作分散在不同的Model中。
所以,實際專案的程式碼不會是上面的樣子。
模擬需求
接收引數:
- 名字
- 性別
- 簽名
業務處理流程:
- 接收引數
- 由發號器得到使用者的uid,發號器對應資料表增加一位數字
- 把名字、性別、簽名和上一步的uid寫入使用者資訊表
- 初始化使用者餘額表
回滾觸發時機:
- 初始化餘額表沒有傳入uid匯出沒有寫入資料
實際程式碼
//Controller
/**
* 測試事務-註冊使用者
*/
public function actionReg()
{
//獲取請求
$request = Yii::$app->request;
//設定返回格式
$response = Yii::$app->response;
$response->format = yiiwebResponse::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)
從上面的實際程式碼可以看出,建立了事務,只要在範圍內,就算是引入的別的Model也能把異常NG返回,完成回滾操作。
一般情況下,整個Yii應用使用了同一個資料庫連線,或者說是使用了單例。
而在yiidbConnection
中,又對事務物件進行了快取:
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 在整個應用的生命週期中,是有延續性的。 這是實現事務巢狀的關鍵和前提。
總結
技術水平的增長來自踩坑,踩坑來自業務增長。
有時間聊聊職業規劃和發展。 : )
參考資料
相關文章
- 編碼式事務管理使用例項
- PHP高階特性-反射Reflection以及Factory工廠設計模式的結合使用[程式碼例項]PHP反射設計模式
- php class中public,private,protected,static的區別,以及例項PHP
- PureComponent 使用注意事項以及原始碼解析原始碼
- MySQL資料庫的事務處理用法與例項程式碼詳解MySql資料庫
- cookie的使用方法以及注意事項Cookie
- php例項化物件的例項方法PHP物件
- Spring事務管理(詳解+例項)Spring
- Bash Shell指令碼中的陣列使用例項指令碼陣列
- PHP程式碼審計03之例項化任意物件漏洞PHP物件
- Spring程式設計式和宣告式事務例項講解Spring程式設計
- 刪除字串中的html標籤程式碼例項字串HTML
- 分散式事務~從seata例項來學習分散式事務分散式
- 低程式碼和無程式碼的注意事項
- Mysql索引以及使用索引注意事項MySql索引
- dom操作程式碼例項
- css梯形程式碼例項CSS
- Yii2多模型與事務的用法模型
- iOS 程式碼注意事項iOS
- 什麼是PHP或PHP程式碼以及PHP程式設計的工作原理PHP程式設計
- Linux 中 ss 命令的使用例項Linux
- Linux 中的 JQ 命令使用例項Linux
- Linux中ip命令的使用例項Linux
- Linux中的basename命令使用例項Linux
- ORACLE事務和例項恢復過程梳理Oracle
- JavaScript in運算子程式碼例項JavaScript
- 設計模式例項程式碼設計模式
- table表格美化程式碼例項
- 純css tab選項卡程式碼例項CSS
- jQuery tab選項卡效果程式碼例項jQuery
- 127 PHP獲取網頁標題的3種實現方法程式碼例項PHP網頁
- vue例項中watch屬性的使用Vue
- JavaScript中常用的事件程式碼及例項JavaScript事件
- 製作PHP的RPM包例項PHP
- 5種PHP生成圖片驗證碼例項PHP
- WPF新建viewModel例項化成員的注意事項View
- Linux中printf命令使用例項Linux
- Flowable啟動流程例項和查詢任務以及完成任務
- 使用無程式碼開發平臺需要重點注意的事項