Java - JPA - аннотация @Version - PullRequest
106 голосов
/ 04 апреля 2010

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

Я нашел разные ответы, выдержка которых следующая:

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

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


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

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

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

Ответы [ 5 ]

176 голосов
/ 04 апреля 2010

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

Допустим, у сущности 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.

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

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

26 голосов
/ 04 ноября 2014

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

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

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

    // ...
}

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

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

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

8 голосов
/ 04 апреля 2010

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

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

2 голосов
/ 22 августа 2011

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

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

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

0 голосов
/ 27 апреля 2017

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

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

Pedro

...