JPA @Version - Почему оптимистическая блокировка не работает при попытке обновить удаленный объект? - PullRequest
1 голос
/ 15 мая 2019

Мне нужно запретить пользователю обновлять сущность, которую другой пользователь уже удалил. Это желаемое поведение:

  1. Пользователь А просматривает запись @ версия 0.
  2. Пользователь Б просматривает запись @ версия 0.
  3. Пользователь A удаляет запись.
  4. Пользователь Б пытается обновить запись. Но пользователь А уже удален, так что это должно произойти.

Я пытаюсь использовать оптимистичное управление параллелизмом через @Version для достижения этой цели.

У меня есть версионная сущность, такая как:

@Entity
public class Foo {

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Long id;

    @Version
    private Integer version;

    @OneToMany(cascade=CascadeType.ALL, orphanRemoval=true)
    @JoinColumn(name="orderId", nullable=false)
    private List<Bar> bars;

Bar не имеет версии:

@Entity
public class Bar {

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Long id;

На шаге 3 пользователь A запрашивает удаление. Транзакция создается и вызывается delete на FooRepository, который реализует CrudRepository:

@RestController
public class FooController {


    private FooService fooService;

    @DeleteMapping
    @ResponseStatus(HttpStatus.NO_CONTENT)
    public void delete(Foo foo) {       

        fooService.delete(foo);
    }

    @PutMapping
    @ResponseStatus(HttpStatus.NO_CONTENT)
    public void update(Foo foo) {       

        fooService.update(foo);
    }


 @Service
 public class FooService {

   private FooRepository fooRepo;

   @Transactional
   public void delete(Foo foo) {
        fooRepo.delete(foo);
   }

   @Transactional
   public void update(Foo foo) {
        fooRepo.save(foo);
   }

На шаге 4 через некоторое время после шага 3 пользователь B вызывает update и

fooRepo.save(foo); будет вызываться в отдельной транзакции

Это приводит к вставке новой строки foo в базу данных?!? foo предоставляется клиентом и имеет тот же идентификатор и версию, которые использовались при удалении. Поскольку у foo есть идентификатор, я подумал, что он попытается обновить, а затем поймет, что не может этого сделать, поскольку объект был удален из БД. Вместо этого он игнорирует идентификатор foo и вставляет в БД новую запись с другим идентификатором?!?!?!?

Как я могу получить вместо этого org.springframework.orm.ObjectOptimisticLockingFailureException?

Рассматривая реализацию save SimpleJpaRepository, entityInformation.isNew(entity) должен возвращать false во время обновления, поскольку идентификатор foo имеет ненулевое значение. Это означает, что менеджер сущностей должен выполнить слияние.

...