Представляю вашему вниманию перевод пятой главы официальной документации Hibernate.
Перевод статьи актуален для версии Hibernate 4.2.19.Final
Предыдущая глава — Документация разработчика Hibernate – Глава IV. Пакетная обработка
Следующая глава — Документация разработчика Hibernate – Глава VI. Кэширование
Содержание
 5.1. Оптимистичные блокировки
   5.1.1 Выделенный номер версии
   5.1.2. Timestamp
 5.2. Пессимистичные блокировки
Блокировки – это меры по предотвращению модификации данных в реляционной базе данных между временем их чтения, и временем их использования.
Стратегия блокировок может быть либо оптимистичной, либо пессимистичной.
Стратегии блокировок
Оптимистичная
Оптимистичные блокировки предполагают, что множество транзакций могут завершиться без влияния друг на друга, и таким образом могут выполнятся без блокировок тех ресурсов, на которые они влияют. Перед коммитом, каждая транзакция проверяет, что ни одна другая транзакция не модифицировала ее данные. Если проверка выявила конфликтующие модификации, транзакция, находящаяся в состоянии коммита, откатывается.
Пессимистичная
Пессимистичная стратегия подразумевает, что параллельные транзакции будут конфликтовать каждая друг с другом, и требует блокировки ресурсов после их чтения, а также ее снятия только после того, как приложение завершило использование данных.
Hibernate предоставляет механизмы для реализации обеих стратегий блокировок в вашем приложении.
Вы можете хранить версионированные данные, когда ваше приложение использует долгоживущие транзакции или диалоги, покрывающие несколько БД-транзакций. Таким образом, если одна и та же сущность будет модифицироваться двумя диалогами, последний диалог, коммитивший изменения, будет оповещен о конфликте, и не перезапишет результаты другого диалога. Этот подход гарантирует некоторую степень изоляции, но при этом хорошо масштабируется, и довольно неплохо себя показывает в ситуациях Read-Often Write-Sometimes
Hibernate предоставляет два различных механизма для хранения версионной информации – выделенный номер версии, или временную метку (timestamp).
Номер версии
Временная метка
Механизм номера версии для оптимистичных блокировок предоставляется аннотацией Version.
Пример 5.1. Аннотация Version
Здесь свойство версии маппится на колонку OPTLOCK, а менеджер сущностей (entity manager) использует ее для выявления конфликтующих обновлений, и предотвращения потери обновлений, которые были бы перезаписаны стратегией last-commit-wins
Колонка версии может быть любого типа, при условии, что вы определите и реализуете подходящий UserVersionType.
Вашему приложению запрещено изменять номер версии, проставленный Hibernate. Чтобы искусственно увеличить номер версии, см. описание свойств LockModeType.OPTIMISTIC_FORCE_INCREMENT или LockModeType.PESSIMISTIC_FORCE_INCREMENT в документации по Hibernate Entity Manager. Если номер версии сгенерирован базой данных, например триггером, используйте аннотацию org.hibernate.annotations.Generated(GenerationTime.ALWAYS).
Пример 5.2. Объявление свойства версии в hbm.xml
Временные метки (timestamps) — менее надежный способ оптимистичных блокировок чем номера версий, который также может быть использован приложениями для других целей. Временные метки используются автоматически, если вы используете аннотацию Version на свойстве типа Date или Calendar.
Пример 5.3. Использование временных меток для оптимистичных блокировок
Hibernate может извлечь значение временной метки из базы данных или JVM, прочитав значение аннотации org.hibernate.annotations.Source. Значение может быть либо org.hibernate.annotations.SourceType.DB, либо org.hibernate.annotations.SourceType.VM. Поведение по-умолчанию – это использование БД, также используемое, если вы не укажете аннотацию.
Временная метка также может быть сгенерирована базой данных вместо Hibernate, если вы используете аннотацию org.hibernate.annotations.Generated(GenerationTime.ALWAYS).
Пример 5.4. Элемент timestamp в hbm.xml
Класс LockMode определяет различные уровни блокировок, которые может захватывать Hibernate.
Явный запрос пользователя, обозначенный выше, происходит как следствие любых из следующих действий:
Если вы вызовете Session.load() с опцией UPGRADE или UPGRADE_NOWAIT, и запрошенный объект еще не подгрузился сессией, объект подгружается с помощью SELECT… FOR UPDATE. Если вы вызовете load() для объекта, которые уже подгружен с менее строгой блокировкой, чем с той, что вы запросили, Hibernate вызовет lock() для этого объекта.
Session.lock() осуществляет проверку номера версии в режимах READ, UPGRADE, или UPGRADE_NOWAIT. В случаях UPGRADE или UPGRADE_NOWAIT, будет использован синтаксис SELECT… FOR UPDATE.
Если запрошенный режим блокировки не поддерживается базой данных, Hibernate будет использовать подходящий альтернативный режим вместо выбрасывания исключения. Это гарантирует переносимость приложений.
Перевод статьи актуален для версии Hibernate 4.2.19.Final
Предыдущая глава — Документация разработчика Hibernate – Глава IV. Пакетная обработка
Следующая глава — Документация разработчика Hibernate – Глава VI. Кэширование
Содержание
 5.1. Оптимистичные блокировки
   5.1.1 Выделенный номер версии
   5.1.2. Timestamp
 5.2. Пессимистичные блокировки
Блокировки – это меры по предотвращению модификации данных в реляционной базе данных между временем их чтения, и временем их использования.
Стратегия блокировок может быть либо оптимистичной, либо пессимистичной.
Стратегии блокировок
Оптимистичная
Оптимистичные блокировки предполагают, что множество транзакций могут завершиться без влияния друг на друга, и таким образом могут выполнятся без блокировок тех ресурсов, на которые они влияют. Перед коммитом, каждая транзакция проверяет, что ни одна другая транзакция не модифицировала ее данные. Если проверка выявила конфликтующие модификации, транзакция, находящаяся в состоянии коммита, откатывается.
Пессимистичная
Пессимистичная стратегия подразумевает, что параллельные транзакции будут конфликтовать каждая друг с другом, и требует блокировки ресурсов после их чтения, а также ее снятия только после того, как приложение завершило использование данных.
Hibernate предоставляет механизмы для реализации обеих стратегий блокировок в вашем приложении.
5.1. Оптимистичные блокировки
Вы можете хранить версионированные данные, когда ваше приложение использует долгоживущие транзакции или диалоги, покрывающие несколько БД-транзакций. Таким образом, если одна и та же сущность будет модифицироваться двумя диалогами, последний диалог, коммитивший изменения, будет оповещен о конфликте, и не перезапишет результаты другого диалога. Этот подход гарантирует некоторую степень изоляции, но при этом хорошо масштабируется, и довольно неплохо себя показывает в ситуациях Read-Often Write-Sometimes
Hibernate предоставляет два различных механизма для хранения версионной информации – выделенный номер версии, или временную метку (timestamp).
Номер версии
Временная метка
Важно
Свойство версии или временной метки не может быть null для отсоединенных (detached) объектов. Hibernate распознает любой экземпляр с версией ( или временной меткой) равной null как transient, в независимости от других стратегий unsaved-value* которые вы указываете. Объявление null-ового свойства версии или временной метки – легкий способ избежать проблемы с транзитивным повторным соединением(transitive reattachment) в Hibernate, являющееся особенно полезным в случаях, где вы используете присоединенные (assigned) идентификаторы или композитные ключи.
* unsaved-value – стратегия определения операции UPDATE или INSERT для синхронизации с БД, зависящая от значения свойства, проецирующегося с помощью id, version, или timestamp (прим. перев.)
* unsaved-value – стратегия определения операции UPDATE или INSERT для синхронизации с БД, зависящая от значения свойства, проецирующегося с помощью id, version, или timestamp (прим. перев.)
5.1.1. Выделенный номер версии
Механизм номера версии для оптимистичных блокировок предоставляется аннотацией Version.
Пример 5.1. Аннотация Version
@Entity
public class Flight implements Serializable {
...
@Version
@Column(name="OPTLOCK")
public Integer getVersion() { ... }
}
Здесь свойство версии маппится на колонку OPTLOCK, а менеджер сущностей (entity manager) использует ее для выявления конфликтующих обновлений, и предотвращения потери обновлений, которые были бы перезаписаны стратегией last-commit-wins
Колонка версии может быть любого типа, при условии, что вы определите и реализуете подходящий UserVersionType.
Вашему приложению запрещено изменять номер версии, проставленный Hibernate. Чтобы искусственно увеличить номер версии, см. описание свойств LockModeType.OPTIMISTIC_FORCE_INCREMENT или LockModeType.PESSIMISTIC_FORCE_INCREMENT в документации по Hibernate Entity Manager. Если номер версии сгенерирован базой данных, например триггером, используйте аннотацию org.hibernate.annotations.Generated(GenerationTime.ALWAYS).
Пример 5.2. Объявление свойства версии в hbm.xml
<version
column="version_column"
name="propertyName"
type="typename"
access="field|property|ClassName"
unsaved-value="null|negative|undefined"
generated="never|always"
insert="true|false"
node="element-name|@attribute-name|element/@attribute|."
/>
Имя | Описание |
---|---|
column | Имя колонки, в которой находится номер версии. Опционально, по-умолчанию такое же как у имени свойства. |
name | Имя свойства персистентного класса. |
type | Тип номера версии. Опционально, по-умолчанию integer. |
access | Стратегия Hibernate для доступа к значению свойства. Опционально, по-умолчанию property |
unsaved-value | Показывает, что экземпляр только что создан и тем самым не сохранен. Выделяет из отсоединенных сущностей (detached). Значение по-умолчанию, undefined, показывает, что свойство-идентификатор не должно использоваться. Опционально. |
generated | Показывает, что свойство версии должно генерироваться базой данных. Опционально, по-умолчанию never. |
insert | Включать или нет колонку версии в выражение SQL-insert. По-умолчанию true, but вы можете поставить это в false если колонка в БД определена со значением по-умолчанию 0 |
5.1.2. Timestamp
Временные метки (timestamps) — менее надежный способ оптимистичных блокировок чем номера версий, который также может быть использован приложениями для других целей. Временные метки используются автоматически, если вы используете аннотацию Version на свойстве типа Date или Calendar.
Пример 5.3. Использование временных меток для оптимистичных блокировок
@Entity
public class Flight implements Serializable {
...
@Version
public Date getLastUpdate() { ... }
}
Hibernate может извлечь значение временной метки из базы данных или JVM, прочитав значение аннотации org.hibernate.annotations.Source. Значение может быть либо org.hibernate.annotations.SourceType.DB, либо org.hibernate.annotations.SourceType.VM. Поведение по-умолчанию – это использование БД, также используемое, если вы не укажете аннотацию.
Временная метка также может быть сгенерирована базой данных вместо Hibernate, если вы используете аннотацию org.hibernate.annotations.Generated(GenerationTime.ALWAYS).
Пример 5.4. Элемент timestamp в hbm.xml
<timestamp
column="timestamp_column"
name="propertyName"
access="field|property|ClassName"
unsaved-value="null|undefined"
source="vm|db"
generated="never|always"
node="element-name|@attribute-name|element/@attribute|."
/>
Имя | Описание |
---|---|
column | Имя колонки, в которой находится временная метка. Опционально, по-умолчанию такое же, как и имя свойства. |
name | Имя JavaBeans-свойства типа Date или Timestamp у персистентного свойства. |
access | Стратегия, которую Hibernate использует для доступа к значению свойства. Опционально, по-умолчанию property. |
unsaved-value | Показывает, что экземпляр только что создан и тем самым не сохранен. Выделяет из отсоединенных сущностей (detached). Значение по-умолчанию, undefined, показывает что свойство-идентификатор не должно использоваться. Опционально. |
source | Извлекает ли Hibernate метку из БД или из текущей JVM. БД-метки вносят дополнительный оверхэд, т.к Hibernate нужно запрашивать БД каждый раз для определения инкремента. Однако, БД-метки более безопасны при использовании в кластеризованном окружении. Не все диалекты БД поддерживают извлечение текущих временных меток из БД. Другие могут быть небезопасны для блокировок, из-за нехватки точности. |
generated | Генерируется ли метки средствами БД. Опционально, по-умолчанию never. |
5.2. Пессимистичные блокировки
Класс LockMode определяет различные уровни блокировок, которые может захватывать Hibernate.
- LockMode.WRITE
Захватывается автоматически, когда Hibernate обновляет или вставляет строку. - LockMode.UPGRADE
Захватывается после явного запроса пользователя с использованием SELECT… FOR UPDATE на БД, поддерживающих данный синтаксис. - LockMode.UPGRADE_NOWAIT
Захватывается после явного запроса пользователя с использованием SELECT… FOR UPDATE NOWAIT в Oracle - LockMode.READ
Захватывается автоматически когда Hibernate читает данные под уровнями изоляции Repeatable Read или Serializable. Может быть повторно захвачен явным запросом пользователя. - LockMode.NONE
Отсутствие блокировки. Все объекты переключаются на этот режим блокировки в конце транзакции. Объекты, ассоциированные с сессией
через вызов update() или saveOrUpdate также начинают в этом режиме .
Явный запрос пользователя, обозначенный выше, происходит как следствие любых из следующих действий:
- Вызов Session.load(), с указанием LockMode
- Вызов Session.lock()
- Вызов Query.setLockMode()
Если вы вызовете Session.load() с опцией UPGRADE или UPGRADE_NOWAIT, и запрошенный объект еще не подгрузился сессией, объект подгружается с помощью SELECT… FOR UPDATE. Если вы вызовете load() для объекта, которые уже подгружен с менее строгой блокировкой, чем с той, что вы запросили, Hibernate вызовет lock() для этого объекта.
Session.lock() осуществляет проверку номера версии в режимах READ, UPGRADE, или UPGRADE_NOWAIT. В случаях UPGRADE или UPGRADE_NOWAIT, будет использован синтаксис SELECT… FOR UPDATE.
Если запрошенный режим блокировки не поддерживается базой данных, Hibernate будет использовать подходящий альтернативный режим вместо выбрасывания исключения. Это гарантирует переносимость приложений.