Как работает @Version
аннотация в JPA?
Я нашел несколько ответов, выдержка из которых выглядит следующим образом:
JPA использует поле версии в ваших объектах для обнаружения одновременных изменений одной и той же записи хранилища данных. Когда среда выполнения JPA обнаруживает попытку одновременного изменения одной и той же записи, она генерирует исключение для транзакции, пытающейся зафиксировать последнюю.
Но я до сих пор не понимаю, как это работает.
Также как из следующих строк:
Вы должны считать поля версии неизменными. Изменение значения поля приводит к неопределенным результатам.
Означает ли это, что мы должны объявить поле версии как final
?
java
jpa
jpa-annotations
Ятендра Гоэль
источник
источник
UPDATE myentity SET mycolumn = 'new value', version = version + 1 WHERE version = [old.version]
. Если кто-то обновил запись,old.version
она больше не будет соответствовать записи в БД, и предложение where предотвратит обновление. Будут обновлены строки0
, которые JPA может обнаружить, чтобы сделать вывод, что произошла одновременная модификация.Ответы:
Допустим, у объекта
MyEntity
есть аннотированноеversion
свойство:При обновлении поле, помеченное значком,
@Version
будет увеличено и добавлено кWHERE
предложению, примерно так:Если
WHERE
предложение не соответствует записи (поскольку та же сущность уже была обновлена другим потоком), то поставщик сохраняемости выдаст файлOptimisticLockException
.Нет, но вы можете подумать о том, чтобы сделать сеттер защищенным, поскольку вы не должны это называть.
источник
long
или просто на вызовlongValue()
. Вам нужны явныеnull
проверки. Я бы не стал делать явную инициализацию его самостоятельно, так как это поле должно управляться поставщиком ORM. Я думаю, что он устанавливается0L
на первую вставку в БД, поэтому, если он установлен,null
это говорит вам, что эта запись еще не сохранена.Long
а не примитивlong
для@Version
поля, поскольку полеSimpleJpaRepository.save(entity)
с нулевой версией, скорее всего, будет рассматриваться как индикатор того, что сущность является новой. Если объект новый (на что указывает значение null), Spring вызоветem.persist(entity)
. Но если у версии есть значение, она вызоветem.merge(entity)
. По этой же причине поле версии, объявленное как тип-оболочка, следует оставить неинициализированным.Хотя ответ @Pascal совершенно верен, по моему опыту я считаю, что приведенный ниже код полезен для выполнения оптимистической блокировки:
Зачем? Так как:
@Version
случайно установлено значениеnull
.optlock
а неversion
.Первая точка не имеет значения , если пользы приложения только JPA для вставки данных в базу данных, так как JPA поставщик будет обеспечивать
0
для@version
поля во время создания. Но почти всегда используются простые операторы SQL (по крайней мере, во время модульного и интеграционного тестирования).источник
u_lmod
(последний измененный пользователем), где пользователь может быть человеком или определенным (автоматизированным) процессом / объектом / приложением (так что дополнительное хранение такжеu_lmod_id
может иметь смысл). (На мой взгляд, это очень простой бизнес-мета-атрибут). Обычно он сохраняет свое значение как ДАТА (ВРЕМЯ) (что означает информацию о году ... миллисекундах) в формате UTC (если «местоположение» часового пояса редактора важно сохранить, то с часовым поясом). Кроме того, очень полезно для сценариев синхронизации DWH.Каждый раз, когда объект обновляется в базе данных, поле версии увеличивается на единицу. Каждая операция, которая обновляет объект в базе данных, будет добавлена
WHERE version = VERSION_THAT_WAS_LOADED_FROM_DATABASE
к запросу.При проверке затронутых строк вашей операции структура jpa может убедиться, что не было одновременных изменений между загрузкой и сохранением вашей сущности, потому что запрос не найдет вашу сущность в базе данных, когда номер версии был увеличен между загрузкой и сохранением.
источник
Версия, используемая для обеспечения только одного обновления за раз. Поставщик JPA проверит версию, если ожидаемая версия уже увеличивается, то кто-то уже обновляет объект, поэтому будет создано исключение.
Таким образом, обновление значения объекта будет более безопасным и оптимистичным.
Если значение часто меняется, вы можете не использовать поле версии. Например, «объект, у которого есть поле счетчика, которое будет увеличиваться каждый раз при доступе к веб-странице»
источник
Просто добавляю еще немного информации.
JPA управляет версией под капотом для вас, однако он не делает этого, когда вы обновляете свою запись через
JPAUpdateClause
, в таких случаях вам необходимо вручную добавить приращение версии в запрос.То же самое можно сказать и об обновлении через JPQL, то есть не о простом изменении объекта, а о команде обновления базы данных, даже если это выполняется с помощью спящего режима.
Pedro
источник
JPAUpdateClause
?