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 相关文章推荐
一个MYSQL操作类
Nov 16 PHP
PHP文件上传原理简单分析
May 29 PHP
PHP中3种生成XML文件方法的速度效率比较
Oct 06 PHP
浅析PHP中的UNICODE 编码与解码
Jun 29 PHP
CodeIgniter上传图片成功的全部过程分享
Aug 12 PHP
PHP在引号前面添加反斜杠(PHP去除反斜杠)
Sep 28 PHP
PHP 使用memcached简单示例分享
Mar 05 PHP
PHP 接入支付宝即时到账功能
Sep 18 PHP
利用PHP绘图函数实现简单验证码功能的方法
Oct 18 PHP
Laravel 集成微信用户登录和绑定的实现
Dec 27 PHP
PHP实现单例模式建立数据库连接的方法分析
Feb 11 PHP
如何在Mac上通过docker配置PHP开发环境
May 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
PHP.MVC的模板标签系统(二)
2006/09/05 PHP
php另类上传图片的方法(PHP用Socket上传图片)
2013/10/30 PHP
php微信高级接口调用方法(自定义菜单接口、客服接口、二维码)
2016/11/28 PHP
YII2框架中使用yii.js实现的post请求
2017/04/09 PHP
最短的javascript:地址栏载入脚本代码
2011/10/13 Javascript
js中一个函数获取另一个函数返回值问题探讨
2013/11/21 Javascript
js如何获取object类型里的键值
2014/02/18 Javascript
jquery文本框中的事件应用以输入邮箱为例
2014/05/06 Javascript
node.js中的http.response.addTrailers方法使用说明
2014/12/14 Javascript
深入解析JavaScript中的arguments对象
2016/06/12 Javascript
Angularjs之filter过滤器(推荐)
2016/11/27 Javascript
node.js操作MongoDB的实例详解
2017/10/11 Javascript
详解redis在nodejs中的应用
2018/05/02 NodeJs
JavaScript数组,JSON对象实现动态添加、修改、删除功能示例
2018/05/26 Javascript
vue中img src 动态加载本地json的图片路径写法
2019/04/25 Javascript
浅谈laytpl 模板空值显示null的解决方法及简单的js表达式
2019/09/19 Javascript
微信小程序使用蓝牙小插件
2019/09/23 Javascript
javascript头像上传代码实例
2019/09/28 Javascript
Vuex实现数据共享的方法
2019/12/20 Javascript
js实现无缝轮播图插件封装
2020/07/31 Javascript
利用PyInstaller将python程序.py转为.exe的方法详解
2017/05/03 Python
详谈python3中用for循环删除列表中元素的坑
2018/04/19 Python
Python3内置模块random随机方法小结
2019/07/13 Python
python实现将文件夹内的每张图片批量分割成多张
2019/07/22 Python
linux下python中文乱码解决方案详解
2019/08/28 Python
4行Python代码生成图像验证码(2种)
2020/04/07 Python
Python实现自动整理文件的脚本
2020/12/17 Python
python实现银行账户系统
2021/02/22 Python
美国知名日用品连锁超市:Dollar General(多来店)
2017/01/14 全球购物
庆七一活动方案
2014/01/25 职场文书
宝宝满月酒主持词和仪式流程
2014/03/27 职场文书
弄虚作假心得体会
2014/09/10 职场文书
就业证明函
2015/06/17 职场文书
四大名著读书笔记
2015/06/25 职场文书
农村结婚典礼主持词
2015/06/29 职场文书
纪检部部长竞选稿
2015/11/21 职场文书