LockModeType

JPA предоставляет оптимистические и пессимистические режимы блокировки. Их поведение зависит от транзакции, диалекта и возможностей БД.

Оптимистическая блокировка

Сущность содержит поле @Version. При обновлении Hibernate добавляет версию в условие:

update account
set balance = ?, version = version + 1
where id = ? and version = ?

Если обновлено ноль строк, другой участник уже изменил запись — возникает optimistic lock exception.

  • OPTIMISTIC проверяет версию;
  • OPTIMISTIC_FORCE_INCREMENT дополнительно увеличивает её;
  • устаревшие имена READ и WRITE являются синонимами этих режимов.

Пессимистическая блокировка

  • PESSIMISTIC_READ — разделяемая блокировка, если БД её поддерживает;
  • PESSIMISTIC_WRITE — блокировка для изменения, часто SELECT ... FOR UPDATE;
  • PESSIMISTIC_FORCE_INCREMENT — блокировка плюс увеличение версии.

Такая блокировка удерживается до завершения транзакции. Возможны ожидание, timeout и deadlock. Порядок захвата строк должен быть единообразным.

Account account = em.find(
    Account.class,
    id,
    LockModeType.PESSIMISTIC_WRITE
);

Как выбирать

  • редкие конфликты — @Version и повтор операции;
  • короткая операция с высокой конкуренцией — пессимистическая блокировка;
  • инвариант по нескольким строкам — сначала спроектировать транзакцию и ограничения БД;
  • внешняя сеть внутри блокировки — почти всегда плохая идея.

Вопросы для повторения

  1. Что обнаруживает @Version? Потерянное обновление одной версии сущности.
  2. Пессимистическая блокировка работает без транзакции? Нет, смысл блокировки задаёт транзакционная граница.
  3. PESSIMISTIC_WRITE блокирует таблицу? Обычно конкретные выбранные строки, но план и СУБД могут изменить масштаб.
  4. Что делать при optimistic conflict? Вернуть конфликт пользователю или безопасно повторить всю бизнес-операцию.
  5. Как уменьшить deadlock? Захватывать ресурсы в одинаковом порядке и сокращать транзакцию.

Связано: Transactional, Изоляция транзакций, Deadlock.