Ответ
При оптимистической блокировке каждый объект сущности имеет поле-версию (аннотированное @Version). При попытке обновления или удаления Hibernate проверяет, не изменилась ли эта версия в базе данных с момента загрузки сущности в persistence context.
Если версии не совпадают (конфликт), возникает исключение OptimisticLockException (или его подкласс StaleObjectStateException). Это указывает, что другая транзакция уже изменила данные.
Пример сущности:
@Entity
public class Account {
@Id
private Long id;
private BigDecimal balance;
@Version
private Long version; // Поле для оптимистической блокировки
}
Что происходит при конфликте:
- Транзакция A загружает
Accountсversion = 1. - Транзакция B загружает тот же
Accountсversion = 1. - Транзакция B изменяет баланс и фиксирует изменения. Hibernate увеличивает
versionдо 2 в БД. - Транзакция A пытается зафиксировать свои изменения. Hibernate выполняет запрос вида:
UPDATE account SET balance=?, version=2 WHERE id=? AND version=1; - Количество обновленных строк будет 0 (так как
versionуже не равен 1). Hibernate обнаруживает это и выбрасываетOptimisticLockException.
Стратегии обработки:
- Информирование пользователя: Поймать исключение, откатить транзакцию и сообщить пользователю, что данные были изменены, предложив повторить операцию.
- Автоматическая повторная попытка: Реализовать механизм retry с повторной загрузкой актуальных данных.
Это предотвращает «потерянные обновления» (lost updates) без использования пессимистических блокировок на уровне БД.