Мне нужно, чтобы мое приложение имело следующее поведение:
Сценарий 1
Пользователь A просматривает заказ.
Пользователь B просматривает тот же заказ.
- Пользователь A удаляет заказ.
- Пользователь B запрашивает обновление заказа.Это должно завершиться неудачей.
Сценарий 2
- Пользователь A просматривает заказ
- Пользователь B просматривает тот же заказ
- Обновления пользователя Aзаказ.
- Пользователь Б запрос на удаление заказа.Это должно завершиться ошибкой.
Используя JPA (Hibernate через Spring Data JPA), я пытаюсь использовать @Version
для реализации этого оптимистического поведения блокировки:
@Entity
public class Order {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
@Version
private Integer version;
// many other fields
При удаленииклиент UI предоставляет серверу список идентификаторов заказов вместе с номером версии для каждого идентификатора заказа.В этом [сообщении ( Spring Data JPA: удалить семантику оптимистической блокировки ) упоминается стандартное решение:
if (entity.getVersion() != dto.getVersion()) {
throw new OptimisticLockException("...");
}
Чтобы использовать это, мне нужно
- lookupобъект из базы данных использует идентификатор заказа от клиента
- сравните версию объекта с версией DTO от клиента
- Выполните удаление.
Проблема в том, что на шаге 2 сущность и версия DTO могут совпадать.Но тогда на шаге 3 версия может отличаться.Есть ли способ заставить hibernate выполнить проверку и обновление как одно атомарное действие, такое как:
delete from [Order] where orderId = ? and version = ?
и бросить StaleObjectStateException
, если ни один не удален.
Обновление
Я нашел два подхода, которые должны работать.Есть ли проблема с одним из этих двух подходов?Второй подход предполагает меньше поездок в базу данных.Как правило, клиент отправляет только один заказ на удаление за раз, поэтому производительность здесь не должна быть проблемой.
Подход 1
Для каждого удаляемого заказа:
Order order = orderRepository.findById(
orderIdFromClient).orElseThrow(() ->
new OptimisticLockException());
if (!order.getVersion().equals(versionFromClient)) {
throw new OptimisticLockException();
}
// We now know the managed entity has the same version
// as the requested version. If some other transaction
// has changed the entity, Hibernate will rollback and
// throw OptimisticLockException.
orderRepository.delete(order);
Подход 2
Добавить метод репозитория Order:
int deleteByIdAndVersion(Long id, Integer version);
Для каждого удаляемого заказа:
int x = orderRepository.deleteByIdAndVersion(orderIdFromClient, versionFromClient);
if (x==0) {
throw new OptimisticLockException();
}