系统研发过程中会有很多地方涉及到自增、自减操作 如:加入版块时,用户版块数自增1,版块用户数自增1;退出版块时,用户版块数要减1,版块用户数也要减1
这里推荐:
- 1.自增可以用
- 2.自减不要用,而是用重新count结果的方式。这样可以确保数据的一致性,并且,实际用户使用过程中,自减比较少的,大多数都是自增,重新count一遍,性能并不会有多少消耗。
$map['uid']=$uid;
$map['fid']=$forum_id;
$res = self::where($map)->find();
//查看版块关注表中是否有记录
self::startTrans();
try{
if ($res) {
if ($res['status'] == 1) { //若有记录并且已加入,则改为退出状态,并将该版块用户数减一
$status = 0;
$now_count=self::where('fid',$forum_id)->where('status',1)->count();
ComForum::where('id', $forum_id)->setField('member_count',$now_count-1);
$forum_count=self::getForumCount($uid);
UserModel::where('uid',$uid)->setField('forum_count',$forum_count-1);
//退出版块日志
action_log($uid,2,'退出版块');
} else { //若有记录并且已退出版块,则改为加入,并将该版块用户数加一
$status = 1;
ComForum::where('id', $forum_id)->setInc('member_count');
UserModel::where('uid',$uid)->setInc('forum_count');
//加入版块日志
action_log($uid,1,'加入版块');
}
self::update(['status'=>$status],$map); //更新操作
} else { //若没有记录则添加加入记录,并将该版块用户数加一
self::add(['uid' => $uid, 'fid' => $forum_id,'status'=>1,'create_time'=>time()]);
ComForum::where('id', $forum_id)->setInc('member_count');
UserModel::where('uid',$uid)->setInc('forum_count');
//加入版块日志
action_log($uid,1,'加入版块');
}
UserTaskNew::newAddToForum($uid); //加入版块新手任务
self::commitTrans();
return true;
}catch (\Exception $e){
self::rollbackTrans();
self::setErrorInfo('操作失败:'.self::getErrorInfo().$e->getMessage());
return false;
}
重点:关于事务中的update、setField、setInc、setDec的操作,执行失败,也不会报exception,事务不会回滚
事务中,对于数据库的更新操作,如果where条件查询不到结果,更新不会被执行,这样执行结果为失败,但不会抛出异常,事务继续正常向下执行。
######面对如上这种情况,该如何处理呢?有如下2中可选方案
- 1、保证where条件肯定能查询到想要的数据。比如根据id查询,id肯定存在的,不然之前就报错了。
- 2、获取执行结果,如果执行结果$res==0,说明更新操作影响了0行,那么可以调用exception('更新执行失败');主动抛出异常,告诉事务,回滚操作,执行失败。
注意:关于积分的自减
积分在用户体系里面尤为特殊且很重要,对于积分,不适合重新count了,这个时候只能用自减。 那么如何保证自减操作不会出现异常呢?比如,积分制不会变负值。 这里有两步:
- 1.数据库字段设置为unsigned(非负数)
- 2.进行自减时,where条件里面加上score>=num(score为积分字段,num为要自减的数值)
如上两步操作完成后,还有个问题要注意:涉及到积分变动,在外围调用的时候都会用事务的写法,这个时候,如果加上where条件时,虽然积分自减失败了,但是数据库执行是正常的,而实际情况应该是执行失败,并回滚所有事务。那怎么办呢?
-
可以考虑检测自减操作影响的行数,如果影响行数为0,说明执行失败,这个时候,代码中可以加上主动抛出异常的操作,异常信息为"扣除用户积分操作失败!",提醒外围进行回滚事务。