Java - JPA - аннотация @Version
Как @Version
аннотации работают в JPA?
Я нашел различные ответы, выдержка которых выглядит следующим образом:
JPA использует поле версии в ваших сущностях для обнаружения одновременных изменений в одной и той же записи хранилища данных. Когда среда выполнения JPA обнаруживает попытку одновременного изменения одной и той же записи, она генерирует исключение для транзакции, пытающейся зафиксировать последнюю.
Но я все еще не уверен, как это работает.
Также как из следующих строк:
Вы должны считать поля версии неизменяемыми. Изменение значения поля приводит к неопределенным результатам.
Означает ли это, что мы должны объявить наше поле версии как final
?
5 ответов
Но все же я не уверен, как это работает?
Скажем, сущность 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
,
Означает ли это, что мы должны объявить наше поле версии как окончательное?
Нет, но вы можете подумать о том, чтобы защитить сеттер, так как вы не должны его вызывать.
Хотя ответ @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 будет применять 0
за @version
поле во время создания. Но почти всегда используются простые операторы SQL (по крайней мере, во время модульного / интеграционного тестирования).
Каждый раз, когда сущность обновляется в базе данных, поле версии увеличивается на единицу. Каждая операция, которая обновляет объект в базе данных, будет добавлена WHERE version = VERSION_THAT_WAS_LOADED_FROM_DATABASE
на его запрос.
При проверке затронутых строк вашей операции среда jpa может убедиться, что не было одновременных изменений между загрузкой и сохранением вашей сущности, потому что запрос не найдет вашу сущность в базе данных, когда ее номер версии увеличен между загрузкой и сохранением.
Версия используется для обеспечения только одного обновления за раз. JPA-провайдер проверит версию: если ожидаемая версия уже увеличивается, тогда кто-то другой уже обновит сущность, поэтому будет выдано исключение.
Таким образом, обновление значения объекта будет более безопасным, более оптимистичным.
Если значение часто меняется, то вы можете не использовать поле версии. Например, "объект, имеющий поле счетчика, которое будет увеличиваться при каждом доступе к веб-странице"
Просто добавив немного больше информации.
JPA управляет версией для вас, но не обновляет вашу запись через JPAUpdateClause()
, в таких случаях вам нужно вручную добавить приращение версии в запрос.
Pedro