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 相关文章推荐
PHP extract 将数组拆分成多个变量的函数
Jun 30 PHP
php中var_export与var_dump的区别分析
Aug 21 PHP
PHP执行zip与rar解压缩方法实现代码
Dec 05 PHP
PHP缓存技术的使用说明
Aug 06 PHP
php判断终端是手机还是电脑访问网站的思路及代码
Apr 24 PHP
10个实用的PHP正则表达式汇总
Oct 23 PHP
php需登录的文件上传管理系统
Mar 21 PHP
PHP邮箱验证示例教程
Jun 01 PHP
利用PHPExcel读取Excel的数据和导出数据到Excel
May 12 PHP
解决安装WampServer时提示缺少msvcr110.dll文件的问题
Jul 09 PHP
php微信开发之图片回复功能
Jun 14 PHP
Ubuntu 16.04中Laravel5.4升级到5.6的步骤
Dec 07 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
phpmyadmin 访问被拒绝的真实原因
2009/06/15 PHP
php实现utf-8和GB2312编码相互转换函数代码
2013/02/07 PHP
关于zend studio 出现乱码问题的总结
2013/06/23 PHP
使用laravel的migrate创建数据表的方法
2019/09/30 PHP
关于PHP5.6+版本“No input file specified”问题的解决
2019/12/11 PHP
限制文本框输入N个字符的js代码
2010/05/13 Javascript
jquery多选项卡效果实例代码(附效果图)
2013/03/23 Javascript
使用CamanJS在Web页面上处理图像的技巧
2015/08/18 Javascript
js实现当前输入框高亮显示的方法
2015/08/19 Javascript
微信小程序 框架详解及实例应用
2016/09/26 Javascript
JavaScript SHA-256加密算法详细代码
2016/10/06 Javascript
js代码实现下拉菜单【推荐】
2016/12/15 Javascript
本地存储localStorage用法详解
2017/07/31 Javascript
Canvas实现微信红包照片效果
2018/08/21 Javascript
JSON生成Form表单的方法示例
2018/11/21 Javascript
微信小程序上线发布流程图文详解
2019/05/06 Javascript
微信小程序实现二维码签到考勤系统
2020/01/16 Javascript
Vue实现小购物车功能
2020/12/21 Vue.js
利用Python获取赶集网招聘信息前篇
2016/04/18 Python
python使用xslt提取网页数据的方法
2018/02/23 Python
python定向爬取淘宝商品价格
2018/02/27 Python
Python for循环中的陷阱详解
2018/07/13 Python
Python批量生成幻影坦克图片实例代码
2019/06/04 Python
Python配置文件处理的方法教程
2019/08/29 Python
司机的工作范围及职责
2013/11/13 职场文书
营业员演讲稿
2013/12/30 职场文书
单位绩效考核方案
2014/05/11 职场文书
2014年个人业务工作总结
2014/11/17 职场文书
党风廉正建设责任书
2015/01/29 职场文书
世界红十字日活动总结
2015/02/10 职场文书
教学质量月活动总结
2015/05/11 职场文书
电影建国大业观后感
2015/06/01 职场文书
难以忽视的真相观后感
2015/06/05 职场文书
房屋产权证明书
2015/06/19 职场文书
vue+iview实现手机号分段输入框
2022/03/25 Vue.js
MySql分区类型及创建分区的方法
2022/04/13 MySQL