Исключение OptimisticLockException
Исключение OptimisticLockException выбрасывается
Doctrine ORM, когда две транзакции пытаются
одновременно изменить одну и ту же сущность.
Оно относится к механизму оптимистичной блокировки:
версия сущности проверяется при записи, и если
она уже была изменена другой транзакцией,
возникает конфликт. Исключение помогает
обнаружить такие ситуации и корректно обработать их,
например, повторив операцию или показав
пользователю сообщение об ошибке.
Синтаксис
<?php
throw new OptimisticLockException(
string $message = '',
Throwable $previous = null
);
?>
Пример
Рассмотрим сущность с полем версии:
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity]
class Article
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(type: 'string')]
private string $title;
#[ORM\Column(type: 'text')]
private string $text;
#[ORM\Version]
#[ORM\Column(type: 'integer')]
private int $version = 1;
public function getTitle(): string
{
return $this->title;
}
public function setTitle(string $title): void
{
$this->title = $title;
}
}
?>
Теперь попробуем сохранить сущность, версия которой устарела. В реальной ситуации это может произойти при параллельной работе двух пользователей. Сымитируем конфликт:
<?php
namespace App\Controller;
use App\Entity\Article;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\OptimisticLockException;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
class ArticleController extends AbstractController
{
#[Route('/article/update', name: 'article_update')]
public function update(EntityManagerInterface $em): Response
{
$article = $em->find(Article::class, 1);
$article->setTitle('abcde');
try {
$em->flush();
} catch (OptimisticLockException $e) {
return new Response($e->getMessage());
}
return new Response('updated');
}
}
?>
Если сущность была изменена другой транзакцией, результат выполнения кода будет примерно таким:
"The optimistic lock failed, version 1 was expected, but is actually 2"
Пример
Обработаем исключение и попробуем повторить операцию с актуальной версией сущности:
<?php
namespace App\Controller;
use App\Entity\Article;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\OptimisticLockException;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
class ArticleController extends AbstractController
{
#[Route('/article/retry', name: 'article_retry')]
public function retry(EntityManagerInterface $em): Response
{
$attempts = 0;
while ($attempts < 3) {
try {
$article = $em->find(Article::class, 1);
$article->setTitle('abcde');
$em->flush();
return new Response('updated');
} catch (OptimisticLockException $e) {
$attempts++;
$em->refresh($article);
}
}
return new Response('conflict');
}
}
?>
Результат выполнения кода при успешном повторе:
"updated"
Смотрите также
-
исключение
PessimisticLockException,
которое возникает при пессимистичной блокировке -
исключение
EntityNotFoundException,
которое выбрасывается, если сущность не найдена -
исключение
UniqueConstraintViolationException,
которое сигнализирует о нарушении уникальности -
класс
FlattenException,
который представляет исключение в виде массива