SELECT FOR UPDATE
Doctrineで実装する方法を提案できますか?
カウンター値を読み取り、それを PHP コードで使用して、他の誰か (別のプロセスから) が同じ値を使用する前に、すぐに値をインクリメントする必要があります。
SELECT FOR UPDATE
Doctrineで実装する方法を提案できますか?
カウンター値を読み取り、それを PHP コードで使用して、他の誰か (別のプロセスから) が同じ値を使用する前に、すぐに値をインクリメントする必要があります。
どうやら、Doctrine 2 は LOCK IN SHARED MODE を MySQL の悲観的読み取りロックで使用しますが、これは SELECT FOR UPDATE とは異なります。
現在の安定版リリースのソースを見ると、Doctrine にはそれを行うネイティブな方法がないようです (Doctrine チームが MySQL にそのタイプのロックを選択した理由はわかりません)。
DQL の場合と同様に、従来のエンティティにマップできるネイティブ SQL を回避策として使用しました。
<?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);
また、トランザクション内でステートメントを使用して機能させる必要があります。
Doctrine 2はエンティティのLocking サポートを実装します:
<?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!";
}
また、raw SQL の実行をスローすることもできます。
$em->getConnection()->exec('LOCK TABLES table_name WRITE;'); //lock for write access
その後
$em->getConnection()->exec('UNLOCK TABLES;');
グーグルからここに来る人への警告。
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();
これは、ロックが取得された後にdoctrineがキャッシュとエンティティオブジェクト自体を更新することを確認する唯一の方法です。
どちら$em->lock()
も$em->refresh()
ここでは機能しません。