Pull to refresh

Документация разработчика Hibernate – Глава V. Блокировки

Reading time 6 min
Views 42K
Представляю вашему вниманию перевод пятой главы официальной документации Hibernate.

Перевод статьи актуален для версии Hibernate 4.2.19.Final

Предыдущая главаДокументация разработчика Hibernate – Глава IV. Пакетная обработка
Следующая главаДокументация разработчика Hibernate – Глава VI. Кэширование

Содержание
&nbsp5.1. Оптимистичные блокировки
&nbsp&nbsp&nbsp5.1.1 Выделенный номер версии
&nbsp&nbsp&nbsp5.1.2. Timestamp
&nbsp5.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 (прим. перев.)

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 будет использовать подходящий альтернативный режим вместо выбрасывания исключения. Это гарантирует переносимость приложений.
Tags:
Hubs:
+8
Comments 0
Comments Leave a comment

Articles