Eloquent模型中乐观锁的实现
在app/Utils/Traits目录下创建OptimisticLockTrait.php,代码如下:
namespace App\Utils\Traits;
use Illuminate\Database\Eloquent\Builder;
trait OptimisticLockTrait
{
/**
* @var array $optimisticConditions
* @var array $bindings
*/
protected $optimisticConditions, $bindings;
/**
* @var string $optimisticConditionRaw
*/
protected $optimisticConditionRaw;
/**
* save 时增加乐观锁条件
* @param Builder $builder
*/
protected function performUpdate(Builder $builder)
{
if (!empty($this->optimisticConditions)) {
foreach ($this->optimisticConditions as $field => $value) {
if (is_array($value)) {
$count = count($value);
if ($count >= 3) {
switch (strtoupper($value[1])) {
case 'IN':
$builder->whereIn($value[0], $value[2]);
break;
case 'NOT IN':
$builder->whereNotIn($value[0], $value[2]);
break;
case 'BETWEEN':
$builder->whereBetween($value[0], $value[2]);
break;
case 'NOT BETWEEN':
$builder->whereNotBetween($value[0], $value[2]);
break;
default:
$builder->where($value[0], $value[1], $value[2]);
}
} else {
$builder->where($value);
}
} else {
$builder->where($field, $value);
}
}
}
// 原始条件注入
if ($this->optimisticConditionRaw)
$builder->whereRaw($this->optimisticConditionRaw, $this->bindings);
return $this->clearOptimistic()->perFormUpdating($builder);
}
/**
* updating with optimistic
*
* @param Builder $builder
* @return bool
*/
protected function perFormUpdating(Builder $builder)
{
// If the updating event returns false, we will cancel the update operation so
// developers can hook Validation systems into their models and cancel this
// operation if the model does not pass validation. Otherwise, we update.
if ($this->fireModelEvent('updating') === false) {
return false;
}
// First we need to create a fresh query instance and touch the creation and
// update timestamp on the model which are maintained by us for developer
// convenience. Then we will just continue saving the model instances.
if ($this->usesTimestamps()) {
$this->updateTimestamps();
}
// Once we have run the update operation, we will fire the "updated" event for
// this model instance. This will allow developers to hook into these after
// models are updated, giving them a chance to do any special processing.
$dirty = $this->getDirty();
$res = 0;
if (count($dirty) > 0) {
$res = $this->setKeysForSaveQuery($builder)->update($dirty);
$this->syncChanges();
$this->fireModelEvent('updated', false);
}
return !empty($res);
}
// 清除乐观锁条件
function clearOptimistic()
{
$this->optimisticConditions = null;
$this->optimisticConditionRaw = null;
return $this;
}
// 设置乐观锁条件字段名列表
function setOptimistic(array $optimisticConditions)
{
$this->optimisticConditions = $optimisticConditions;
return $this;
}
// 设置乐观锁原始条件字段名列表
function setOptimisticRaw(string $optimisticConditionRaw, array $bindings = [])
{
$this->optimisticConditionRaw = $optimisticConditionRaw;
$this->bindings = $bindings;
return $this;
}
}
乐观锁使用说明
1、在模型中(Models)或模型父类使用
/**
* App\Models\BaseModel
* @mixin \Eloquent
* @method static \Illuminate\Database\Eloquent\Builder|BaseModel newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|BaseModel newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|BaseModel query()
*/
class BaseModel extends Model
{
use OptimisticLockTrait;
}
2、使用方法:
$ord = Order::find(1);
$ord->payment_status = 1;
if(!$model->setOptimistic(['payment_status' => 0]))->save())
throws new Exception('订单已付过款了');
或者使用原始SQL方式:
$ord = Order::find(1);
$ord->payment_status = 1;
if(!$model->setOptimisticRaw('payment_status = ?',[1]))->save())
throws new Exception('订单已付过款了');
如果同一对象小涉及到多次更新,则可以清除锁条件
$ord->clearOptimistic();
以上就是乐观锁的实现方式,在实际开发中比较常用也很有必要。
本作品采用《CC 协议》,转载必须注明作者和本文链接
原理是 根据 setOptimistic 的 where条件 加的锁吗? 这样范围会不会太大了? 不需要加专门的字段吗 比如version?