【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 在整個應用的生命週期中,是有延續性的。 這是實現事務巢狀的關鍵和前提。
總結
技術水平的增長來自踩坑,踩坑來自業務增長。
有時間聊聊職業規劃和發展。 : )
參考資料
相關文章
- Spring中@Transactional事務回滾例項及原始碼Spring原始碼
- php class中public,private,protected,static的區別,以及例項PHP
- PHP高階特性-反射Reflection以及Factory工廠設計模式的結合使用[程式碼例項]PHP反射設計模式
- $$和||使用程式碼例項
- PureComponent 使用注意事項以及原始碼解析原始碼
- [Sqlite] 嵌入式資料庫事務理解以及例項操作SQLite資料庫
- MySQL資料庫的事務處理用法與例項程式碼詳解MySql資料庫
- js使用XMLHttpRequest例項程式碼JSXMLHTTP
- table表格使用程式碼例項
- javascript Function()使用程式碼例項JavaScriptFunction
- JavaScript 中 this 的工作原理以及注意事項JavaScript
- JavaScript中this的工作原理以及注意事項JavaScript
- cookie的使用方法以及注意事項Cookie
- 使用jQuery去除陣列中的重複元素程式碼例項jQuery陣列
- js with語句使用程式碼例項JS
- js 物件反射使用程式碼例項JS物件反射
- angularJS的$attrs方法使用程式碼例項AngularJS
- Yii2多模型與事務的用法模型
- php getallheaders使用注意事項PHPHeader
- javascript的for in例項程式碼JavaScript
- Spring事務管理(詳解+例項)Spring
- 提取字串中數字的程式碼例項字串
- 在頁面中插入flash的程式碼例項
- php例項化物件的例項方法PHP物件
- Bash Shell指令碼中的陣列使用例項指令碼陣列
- js prototype屬性使用程式碼例項JS
- jQuery css()函式使用程式碼例項jQueryCSS函式
- jquery next()方法使用程式碼例項jQuery
- 使用數字分隔字串程式碼例項字串
- js刪除陣列中重複項的程式碼例項JS陣列
- php在yii2中的cookie用法PHPCookie
- jQuery設定select選中項程式碼例項jQuery
- 使用js刪除字串中的最後一個字元例項程式碼JS字串字元
- Mysql索引以及使用索引注意事項MySql索引
- 【原創】Oracle 事務探索與例項(二)Oracle
- 【原創】 Oracle 事務探索與例項(一)Oracle
- jQuery is() 程式碼例項jQuery
- 刪除陣列中的指定元素例項程式碼陣列