Doctrine2 ORM选择更新

时间:2012-10-19 09:28:02

标签: php mysql concurrency doctrine

请问您能否告诉我如何使用Doctrine实现SELECT FOR UPDATE

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

3 个答案:

答案 0 :(得分:39)

显然,Doctrine 2使用LOCK IN SHARED MODE和MySQL的悲观读锁,这与SELECT FOR UPDATE不同。

看看当前稳定版本的来源,似乎没有本土方式 在Doctrine中这样做(我不确定为什么Doctrine团队为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();

更新

正如本杰明指出的那样,PESSIMISTIC_WRITE就是你要找的。

使用DQL

<?php
$query = $this->em->createQuery('SELECT e
    FROM Application\Model\Entity\MyEntity e
    WHERE e = :id');

$query->setParameter("id", $id);
$query->setLockMode(\Doctrine\DBAL\LockMode::PESSIMISTIC_WRITE);

没有DQL

<?php
$entity = $em->find('Application\Model\Entity\MyEntity', $id, \Doctrine\DBAL\LockMode::PESSIMISTIC_WRITE);

此外,您必须使用事务中的语句才能使其正常工作。

答案 1 :(得分:6)

锁定支持

Doctrine 2为实体实现Locking support

<?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!";
}

Native sql

另外,你可以抛出执行原始SQL:

$em->getConnection()->exec('LOCK TABLES table_name WRITE;'); //lock for write access

然后

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

答案 2 :(得分:0)

警告任何从谷歌来到这里的人。

如果你对现有实体使用 Doctrine 的 PESSIMISTIC_WRITE 锁,
那么该实体在锁定后将不会被重新获取。

所以这段代码:

$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();

这是确保学说在获取锁后更新其缓存以及实体对象本身的唯一方法。

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