Yii2中事务的使用实例代码详解


Posted in PHP onSeptember 07, 2016

前言

一般我们做业务逻辑,都不会仅仅关联一个数据表,所以,会面临事务问题。

数据库事务(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 \yii\db\Exception(); //手动抛出异常,再由下面捕获。
}
//提交
$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 = \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)

从上面的实际代码可以看出,创建了事务,只要在范围内,就算是引入的别的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();
// 缓存的事务对象有效,则使用缓存中的事务对象
// 否则创建一个新的事务对象
if (($transaction = $this->getTransaction()) === null) {
$transaction = $this->_transaction = new Transaction(['db' => $this]);
}
$transaction->begin($isolationLevel);
return $transaction;
}
}

因此,可以认为整个Yii应用,使用了同一个 Transaction 对象,也就是说, Transaction::_level 在整个应用的生命周期中,是有延续性的。 这是实现事务嵌套的关键和前提。

以上所述是小编给大家介绍的Yii2中事务的使用实例代码详解,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

PHP 相关文章推荐
中英文字符串翻转函数
Dec 09 PHP
PHP字符串长度计算 - strlen()函数使用介绍
Oct 15 PHP
php上传图片之时间戳命名(保存路径)
Aug 15 PHP
CentOS下PHP7的编译安装及MySQL的支持和一些常见问题的解决办法
Dec 17 PHP
PHP文件及文件夹操作之创建、删除、移动、复制
Jul 13 PHP
ThinkPHP3.2.1图片验证码实现方法
Aug 19 PHP
thinkPHP框架对接支付宝即时到账接口回调操作示例
Nov 14 PHP
PHP进程通信基础之信号
Feb 19 PHP
PHP去除字符串最后一个字符的三种方法实例
Mar 01 PHP
layui框架实现文件上传及TP3.2.3(thinkPHP)对上传文件进行后台处理操作示例
May 12 PHP
laravel获取不到session的三种解决办法【推荐】
Sep 16 PHP
详解no input file specified 三种解决方法
Nov 29 PHP
PHP模糊查询的实现方法(推荐)
Sep 06 #PHP
浅谈PHP中的数据传输CURL
Sep 06 #PHP
PHP实现页面静态化的超简单方法
Sep 06 #PHP
基于PHP实现短信验证码接口(容联运通讯)
Sep 06 #PHP
PHP7 新特性详细介绍
Sep 06 #PHP
PHP会员找回密码功能的简单实现
Sep 05 #PHP
浅谈php中urlencode与rawurlencode的区别
Sep 05 #PHP
You might like
模拟xcopy的函数
2006/10/09 PHP
PHP获取网卡地址的代码
2008/04/09 PHP
PHP输出九九乘法表代码实例
2015/03/27 PHP
JS图片切换的具体方法(带缩略图版)
2013/11/12 Javascript
js 通过cookie实现刷新不变化树形菜单
2014/10/30 Javascript
JavaScript动态修改网页元素内容的方法
2015/03/21 Javascript
javascript格式化指定日期对象的方法
2015/04/21 Javascript
基于Bootstrap重置输入框内容按钮插件
2016/05/12 Javascript
JS获取子窗口中返回的数据实现方法
2016/05/28 Javascript
最实用的jQuery分页插件
2016/10/09 Javascript
EditPlus中的正则表达式 实战(4)
2016/12/15 Javascript
node.js发送邮件email的方法详解
2017/01/06 Javascript
Vue指令的钩子函数使用方法
2017/03/20 Javascript
vue.js中v-on:textInput无法执行事件问题的解决过程
2017/07/12 Javascript
彻底搞懂JavaScript中的apply和call方法(必看)
2017/09/18 Javascript
用jquery获取select标签中选中的option值及文本的示例
2018/01/25 jQuery
AngularJS中重新加载当前路由页面的方法
2018/03/09 Javascript
JavaScript实现异步图像上传功能
2018/07/12 Javascript
vue项目刷新当前页面的三种方法
2018/12/04 Javascript
详解js实时获取并显示当前时间的方法
2019/05/10 Javascript
layui弹出框Tab选项卡的示例代码
2019/09/04 Javascript
Vue.js watch监视属性知识点总结
2019/11/11 Javascript
使用js实现单链解决前端队列问题的方法
2020/02/03 Javascript
JavaScript this指向相关原理及实例解析
2020/07/10 Javascript
vue3.0中友好使用antdv示例详解
2021/01/05 Vue.js
Python脚本简单实现打开默认浏览器登录人人和打开QQ的方法
2016/04/12 Python
Django中ORM表的创建和增删改查方法示例
2017/11/15 Python
Django中的Signal代码详解
2018/02/05 Python
在python 不同时区之间的差值与转换方法
2019/01/14 Python
美国农场鲜花速递:The Bouqs
2018/07/13 全球购物
匡威意大利官方商店 :Converse意大利
2018/11/27 全球购物
环境工程专业自荐信范文
2014/03/18 职场文书
2014法制宣传日活动总结范文
2014/11/01 职场文书
党的群众路线教育实践活动个人整改措施材料
2014/11/04 职场文书
优秀党员推荐材料
2014/12/18 职场文书
新生儿未入户证明
2015/06/23 职场文书