安全地减少用户余额列.我应该使用乐观锁吗?

Ale*_* P. 9 php mysql sql concurrency doctrine-orm

我有一个简单的Silex Web应用程序与MySQL/Doctrine ORM.每个用户都有balance(这是一个简单的应用程序,所以只有列很好)我需要在一些操作后减少它(当然检查它是> 0).

据我所知,我可以使用乐观锁定来避免冲突/漏洞.我已阅读文档http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/transactions-and-concurrency.html但我找不到任何关于使用它的完整示例.

我在哪里可以获得"预期版本"?我是否需要将其作为输入传递(隐藏表单字段)?还是有更好的方法?文档说了一些关于会话的内容,但我不知道如何将它存储在那里(每个请求都更新会话?).

此外,如果我将其作为输入传递,那么据我所知,在OptimisticLockException没有通知用户的情况下捕获后无法自动重复查询?(例如,如果用户打开两个选项卡并逐个提交请求)

我的目标是在用户同时发送多个请求并且平衡仅减少一次等时防止潜在问题.因此,如果能够在不涉及用户的情况下自动重复锁定错误,那将是一件好事.因为如果我通过表单传递它,那么很可能因为多个选项卡而出现此错误.所以看起来有点复杂,也许还有其他东西而不是乐观的锁定?

小智 7

You should only use locking for operations that can't be executed atomically. So if possible avoid querying the object, checking the amount and then updating it. If instead you do:

update user set balance = (balance + :amount) 
where (balance + :amount) >= 0 
and id = :user_id
Run Code Online (Sandbox Code Playgroud)

This you will check and update in one operation, updated rows count will be 1 if the check passed and the balance was updated and 0 otherwise.


sud*_*dip 5

在"user"表中创建一个名为"version"的列,并将其设置为"timestamp"列(使用"on update CURRENT_TIMESTAMP"属性).所以,"用户"ORM类将如下所示:

class User
{
    // ...
    /** @Version @Column(type="timestamp") */
    private $version;
    // ...
}
Run Code Online (Sandbox Code Playgroud)

现在,用"版本"读取当前记录.

$theEntityId = YOUR ENTITY ID;
$entity = $em->find('User', $theEntityId);
$expectedVersion = entity->version;
try {
   // assert version
    $em->lock($entity, LockMode::OPTIMISTIC, $expectedVersion);

    // do the work

    $em->flush();
} 
catch(OptimisticLockException $e) {
    echo "Sorry, but someone else has already changed this entity. Please apply the changes again!";
}
Run Code Online (Sandbox Code Playgroud)