2010-04-03 11 views
83

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

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

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

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


Кроме того, как один из следующих линий:

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

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

+0

Все это делает чек/обновить версию в каждом запросе обновления: 'UPDATE MyEntity SET MyColumn = 'новое значение', версия = версия + 1 WHERE версия = [old.version]'. Если кто-то обновил запись, 'old.version' больше не будет соответствовать таковой в БД, а предложение where предотвратит это обновление. «Обновленные строки» будут «0», которые JPA может обнаружить, чтобы сделать вывод о том, что произошло параллельное изменение. –

ответ

145

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

Допустим, предприятие 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.

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

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

+5

Я получил исключение с нулевым указателем с этой частью кода, потому что длинные инициализации как null, вероятно, должны быть инициализированы с помощью 0L. – Markus

+1

Не полагайтесь на автоматическое распаковывание его на 'long' или просто называя' longValue() 'на нем. Вам нужны явные проверки «null». Явно инициализировав его самостоятельно, я бы этого не сделал, поскольку это поле должно управляться провайдером ORM. Я думаю, что он устанавливается в '0L' при первой вставке в БД, поэтому, если он установлен в' null', это говорит о том, что эта запись еще не была сохранена. –

+0

Будет ли это предотвращать создание объекта (пытающегося), созданного более одного раза? Я имею в виду, что сообщение о теме JMS запускает создание сущности, и несколько экземпляров приложения прослушивают сообщение. Я просто хочу избежать уникальной ошибки нарушения ограничений .. – Manu

8

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

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

+0

Не через JPAUpdateQuery это не так. Итак, «Каждая операция, которая обновляет объект в базе данных, будет добавлена ​​к версии WHERE version = VERSION_THAT_WAS_LOADED_FROM_DATABASE», это неверно. –

1

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

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

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

15

Хотя @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. Поскольку это специальное поле не обязательно является коммерческой версией объекта, чтобы избежать вводящего в заблуждение, я предпочитаю называть это поле таким, как optlock, а не version.

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

+0

Я называю это ** 'u_lmod' ** (пользователь изменен последним), где ** пользователь может быть человеком или определенным (автоматизированным) процессом/сущностью/приложением ** (так что хранение дополнительно« u_lmod_id »может иметь смысл). (Это, на мой взгляд, очень простой бизнес-мета-атрибут). Обычно он сохраняет значение DATE (TIME) (значение информации о году ...миллисекунды) в UTC (если часовой пояс «местоположение» редактора важно сохранить тогда с часовым поясом). дополнительно очень полезно для сценариев синхронизации DWH. –

+4

Я думаю, что bigint вместо целого числа следует использовать в типе db для соответствия длинному java-типу. – Dmitry

+0

Хороший вопрос Дмитрий, спасибо, хотя, когда дело доходит до версий IMHO, это не имеет большого значения. –

0

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

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

Pedro

+1

Что такое 'JPAUpdateClause'? –