Php Doctrine2 ORM选择进行更新

Php Doctrine2 ORM选择进行更新,php,mysql,concurrency,doctrine,Php,Mysql,Concurrency,Doctrine,您能否建议一种方法,如何使用条令实施选择更新 我需要读取一个计数器值,然后在PHP代码中使用它,并在其他人(来自另一个进程)使用相同的值之前立即增加该值。锁定支持 第2条原则适用于实体: <?php use Doctrine\DBAL\LockMode; use Doctrine\ORM\OptimisticLockException; $theEntityId = 1; $expectedVersion = 184; try { $entity = $em->find(

您能否建议一种方法,如何使用条令实施
选择更新

我需要读取一个计数器值,然后在PHP代码中使用它,并在其他人(来自另一个进程)使用相同的值之前立即增加该值。

锁定支持 第2条原则适用于实体:

<?php
use Doctrine\DBAL\LockMode;
use Doctrine\ORM\OptimisticLockException;

$theEntityId = 1;
$expectedVersion = 184;

try {
    $entity = $em->find('User', $theEntityId, LockMode::OPTIMISTIC, $expectedVersion);

    // do the work

    $em->flush();
} catch(OptimisticLockException $e) {
    echo "Someone else has already changed this entity. Apply the changes again!";
}
然后

$em->getConnection()->exec('UNLOCK TABLES;');

显然,Doctrine 2在共享模式下使用锁,并对MySQL使用悲观读锁,这与SELECT for UPDATE不同

从当前稳定版本的来源来看,似乎没有本地方式 在条令中这样做的可能性(我不知道条令团队为什么选择MySQL的那种锁)

我使用本机SQL作为解决方案,它可以映射到传统实体,就像DQL一样:

<?php
$rsm = new ResultSetMappingBuilder($this->_em);
$rsm->addRootEntityFromClassMetadata('Model_Record_Delivery', 'u');
$query = $this->_em->createNativeQuery("SELECT * FROM delivery WHERE id = :id FOR UPDATE", $rsm);
$query->setParameter("id", $id);
$result = $query->getOneOrNullResult();

警告来自谷歌的任何人

如果对现有实体使用条令的悲观写锁,
然后,锁定后将不会重新蚀刻该实体

所以这个代码:

$entity = $this->em->find(Product::class, $id);
// use the product for some read only code

// Later, Need to update product
$this->em->lock($entity, LockMode::PESSIMISTIC_WRITE);
$entity->setStock($entity->getStock() - 1);
$this->em->flush();
将在SQL中运行类似于以下代码的内容

SELECT t0.id AS id_1, t0.stock AS stock_2 FROM products t0 WHERE t0.id = ?; -- First fetch
SELECT 1 FROM products t0 WHERE t0.id = ? FOR UPDATE; -- Pessimistic lock, no data fetched
UPDATE products SET stock = ? WHERE id = ?; -- Update using old data
这会产生与根本不锁定任何内容相同的结果

您需要在请求锁定的同时再次手动获取实体:

$entity = $this->em->find(Product::class, $id);
// use the product for some read only code

// Need to update product
$this->em->find(Product::class, $entity->getId(), LockMode::PESSIMISTIC_WRITE); // You dont need the return value, doctrine will update all loaded entities
$entity->setStock($entity->getStock() - 1);
$this->em->flush();
这是确保doctrine在获得锁后将更新其缓存以及实体对象本身的唯一方法


$em->lock()
$em->refresh()
在这里都不起作用。

MySQL的SELECT FOR UPDATE与您的原生SQL解决方案有很大不同:它使用一个“特殊”行级锁,它只与其他SELECT FOR UPDATE语句(以及其他,请参阅MySQL文档)一起工作,使用事务时不需要解锁(请参阅:)我刚刚检查了我的SQL日志,在使用
悲观写入
锁定模式时,条令2确实使用了
选择更新
。刚刚重新检查了条令源,是的,悲观写入实际上生成了选择更新,很好!
$entity = $this->em->find(Product::class, $id);
// use the product for some read only code

// Later, Need to update product
$this->em->lock($entity, LockMode::PESSIMISTIC_WRITE);
$entity->setStock($entity->getStock() - 1);
$this->em->flush();
SELECT t0.id AS id_1, t0.stock AS stock_2 FROM products t0 WHERE t0.id = ?; -- First fetch
SELECT 1 FROM products t0 WHERE t0.id = ? FOR UPDATE; -- Pessimistic lock, no data fetched
UPDATE products SET stock = ? WHERE id = ?; -- Update using old data
$entity = $this->em->find(Product::class, $id);
// use the product for some read only code

// Need to update product
$this->em->find(Product::class, $entity->getId(), LockMode::PESSIMISTIC_WRITE); // You dont need the return value, doctrine will update all loaded entities
$entity->setStock($entity->getStock() - 1);
$this->em->flush();