Java - JPA - аннотация @Version

Как работает аннотация @Version в JPA?

Я нашел различные ответы, экстракт которых выглядит следующим образом:

JPA использует поле версии в ваших сущностях для обнаружения одновременных изменений в одной и той же записи хранилища данных. Когда среда выполнения JPA обнаруживает попытку одновременного изменения одной и той же записи, она выдает исключение транзакции, пытающейся совершить последнюю.

Но я все еще не уверен, как это работает.


Также как из следующих строк:

Вы должны рассматривать неизменяемые поля версии. Изменение значения поля имеет undefined.

Означает ли это, что мы должны объявить наше поле версии как final?

Ответ 1

Но я не уверен, как это работает?

Скажем, сущность MyEntity имеет аннотированное свойство version:

@Entity
public class MyEntity implements Serializable {    

    @Id
    @GeneratedValue
    private Long id;

    private String name;

    @Version
    private Long version;

    //...
}

При обновлении поле, аннотированное с помощью @Version, будет увеличиваться и добавляться в предложение WHERE, примерно так:

UPDATE MYENTITY SET ..., VERSION = VERSION + 1 WHERE ((ID = ?) AND (VERSION = ?))

Если предложение WHERE не соответствует записи (поскольку тот же объект уже был обновлен другим потоком), то поставщик сохранения будет вызывать OptimisticLockException.

Означает ли это, что мы должны объявить наше поле версии окончательным

Нет, но вы можете подумать о том, чтобы сделать сеттер защищенным, так как вы не должны его называть.

Ответ 2

Хотя ответ @Pascal совершенно верен, по моему опыту я нахожу код ниже полезным для достижения оптимистической блокировки:

@Entity
public class MyEntity implements Serializable {    
    // ...

    @Version
    @Column(name = "optlock", columnDefinition = "integer DEFAULT 0", nullable = false)
    private long version = 0L;

    // ...
}

Почему? Потому что:

  • Оптимистическая блокировка не будет работать, если поле, аннотированное с помощью @Version, случайно установлено на null.
  • Поскольку это специальное поле не обязательно является бизнес-версией объекта, чтобы избежать вводящего в заблуждение, я предпочитаю называть такое поле таким, как optlock, а не version.

В первую очередь не имеет значения, использует ли приложение JPA только только JPA для вставки данных в базу данных, поскольку поставщик JPA будет применять во время создания 0 для @Version. Но почти всегда используются простые инструкции SQL (по крайней мере, во время тестирования модулей/интеграции).

Ответ 3

Каждый раз, когда объект обновляется в базе данных, поле версии будет увеличено на единицу. Каждая операция, которая обновляет сущность в базе данных, добавит WHERE version = VERSION_THAT_WAS_LOADED_FROM_DATABASE к ее запросу.

При проверке затронутых строк вашей работы инфраструктура jpa может убедиться, что между загрузкой и сохранением вашей сущности не произошло одновременной модификации, потому что запрос не нашел вашу сущность в базе данных, когда ее номер версии был увеличен между загрузкой и сохранением.

Ответ 4

Версия, используемая для обеспечения только одного обновления за раз. Поставщик JPA проверяет версию, если ожидаемая версия уже увеличивается, а кто-то еще уже обновляет объект, поэтому будет выведено исключение.

Таким образом, обновление значения сущности было бы более безопасным, более оптимистичным.

Если значение изменяется часто, вы можете не использовать поле версии. Например, "объект, у которого есть поле счетчика, которое будет увеличиваться каждый раз при просмотре веб-страницы"

Ответ 5

Просто добавив немного больше информации.

JPA управляет версией изнутри за вас, но не обновляет вашу запись через JPAUpdateClause, в таких случаях вам нужно вручную добавить приращение версии в запрос.

Если ваш JPAUpdateClause влияет только на одну запись, вы должны также JPAUpdateClause фильтрацию по version == currentVersion и если запрос возвращает 0, throw new OptimisticLockException()

Если вы массово обновляете, то нет способа сделать массовое обновление с использованием оптимистической блокировки, но все равно следует обновить версию, чтобы другие параллельные сохранения сохраняли оптимистическую блокировку (вы получите половину оптимистической блокировки, которая лучше, чем ничего)

Pedro