Как реализовать оптимистичную блокировку в Hibernate с помощью поля @Version?

«Как реализовать оптимистичную блокировку в Hibernate с помощью поля @Version?» — вопрос из категории Hibernate, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Да, в Hibernate для реализации оптимистичной блокировки используется специальное поле, аннотированное @Version. Этот механизм предотвращает "потерянные обновления" (lost updates) в многопользовательских средах без физической блокировки записей в БД.

Принцип работы:

  1. При загрузке сущности Hibernate запоминает значение поля version.
  2. При обновлении (UPDATE) в WHERE-условие добавляется проверка AND version = [запомненное_значение].
  3. Если версия в БД изменилась (другой поток/транзакция уже обновил запись), условие не срабатывает и количество обновленных строк = 0.
  4. Hibernate обнаруживает это и выбрасывает OptimisticLockException.

Пример реализации:

@Entity
public class Account {
    @Id
    private Long id;
    private BigDecimal balance;

    @Version // Обязательно: тип Integer, Long, int, long, Timestamp
    private Long version; // Hibernate будет инкрементировать это поле автоматически

    // getters and setters
}

Что происходит в БД:

-- При обновлении Hibernate сгенерирует примерно такой запрос:
UPDATE account 
SET balance = 1500.00, version = 2 -- версия увеличивается
WHERE id = 123 AND version = 1; -- проверка исходной версии

Преимущества перед пессимистичной блокировкой (SELECT ... FOR UPDATE):

  • Высокая производительность: Нет долгих блокировок, читающие транзакции не блокируются.
  • Масштабируемость: Подходит для систем с высокой конкурентностью и короткими транзакциями.

Недостаток: При конфликте приложение должно обработать исключение и повторить операцию (retry logic).