32. Какие шесть режимов блокировок (lock modes) описаны спецификации JPA (или какие есть значения у enum LockModeType в JPA)?
UNKNOWNУ JPA есть шесть режимов блокировок (lock modes), перечислим их в порядке увеличения надежности (от самого ненадежного и быстрого, до самого надежного и медленного):
- NONE — без блокировки.
- OPTIMISTIC (или синоним READ, оставшийся от JPA 1) — оптимистическая блокировка.
- OPTIMISTIC_FORCE_INCREMENT (или синоним WRITE, оставшийся от JPA 1) — оптимистическая блокировка с принудительным увеличением поля версионности.
- PESSIMISTIC_READ — пессимистичная блокировка на чтение.
- PESSIMISTIC_WRITE — пессимистичная блокировка на запись (и чтение).
- PESSIMISTIC_FORCE_INCREMENT — пессимистичная блокировка на запись (и чтение) с принудительным увеличением поля версионности.
Оптимистичное блокирование
Оптимистичный подход предполагает, что параллельно выполняющиеся транзакции редко обращаются к одним и тем же данным и позволяет им спокойно и свободно выполнять любые чтения и обновления данных. Но при окончании транзакции производится проверка, изменились ли данные в ходе выполнения данной транзакции и, если да, транзакция обрывается и выбрасывается исключение.
Оптимистичное блокирование в JPA реализовано путём внедрения в сущность специального поля версии:
@Entity
public class Company extends AbstractIdentifiableObject {
@Version
private long version;
@Getter
@Setter
private String name;
@Getter
@Setter
@ManyToMany(mappedBy = \"workingPlaces\")
private Collection<Person> workers;
}
Поле, аннотирование @Version, может быть целочисленным или временнЫм.
При завершении транзакции, если сущность была оптимистично заблокирована, будет проверено, не изменилось ли значение @Version кем-либо ещё, после того как данные были прочитаны, и, если изменилось, будет выкинуто OptimisticLockException.
Использование этого поля позволяет отказаться от блокировок на уровне базы данных и сделать всё на уровне JPA, улучшая уровень конкурентности.
JPA поддерживает два типа оптимистичной блокировки:
- LockModeType.OPTIMISTIC — блокировка на чтение, которая работает, как описано выше: если при завершении транзакции кто-то извне изменит поле @Version, то транзакция автоматически будет откачена и будет выброшено OptimisticLockException.
- LockModeType.OPTIMISTIC_FORCE_INCREMENT — блокировка на запись. Ведёт себя как и блокировка на чтение, но при этом увеличивает значение поля @Version.
Обе блокировки ставятся путём вызова метода lock() у EntityManager, в который передаётся сущность, требующая блокировки и уровень блокировки:
EntityManager em = entityManagerFactory.createEntityManager();
em.lock(company1, LockModeType.OPTIMISTIC);
em.lock(company2, LockModeType.OPTIMISTIC_FORCE_INCREMENT);
Блокировка будет автоматически снята при завершении транзакции, снять её до этого вручную невозможно.
Пессимистичное блокирование
Пессимистичный подход напротив, ориентирован на транзакции, которые постоянно или достаточно часто конкурируют за одни и те же данные и поэтому блокирует доступ к данным превентивно, в тот момент когда читает их. Другие транзакции останавливаются, когда пытаются обратиться к заблокированным данным и ждут снятия блокировки (или кидают исключение). Пессимистичное блокирование выполняется на уровне базы и поэтому не требует вмешательств в код сущности. Так же, как и в случае с оптимистичным блокированием, поддерживаются блокировки чтения и записи:
- LockModeType.PESSIMISTIC_READ — данные блокируются в момент чтения и это гарантирует, что никто в ходе выполнения транзакции не сможет их изменить. Остальные транзакции, тем не менее, смогут параллельно читать эти данные. Использование этой блокировки может вызывать долгое ожидание блокировки или даже выкидывание PessimisticLockException.
- LockModeType.PESSIMISTIC_WRITE — данные блокируются в момент записи и никто с момента захвата блокировки не может в них писать и не может их читать до окончания транзакции, владеющей блокировкой. Использование этой блокировки может вызывать долгое ожидание блокировки.
Кроме того, для сущностей с полем, аннотированным @Version, существует третий вариант пессимистичной блокировки:
- LockModeType.PESSIMISTIC_FORCE_INCREMENT — ведёт себя как LockModeType.PESSIMISTIC_WRITE, но в конце транзакции увеличивает значение поля @Version, даже если фактически сущность не изменилась.
Накладываются пессимистичные блокировки так же как и оптимистичные, вызовом метода lock():
em.lock(company1, LockModeType.PESSIMISTIC_READ);
em.lock(company2, LockModeType.PESSIMISTIC_WRITE);
em.lock(company3, LockModeType.PESSIMISTIC_FORCE_INCREMENT);
Снимаются они тоже автоматически, по завершению транзакции.
Следующие публичные методы EntityManager-а могут использоваться для наложения блокировок:
void lock(Object entity, LockModeType lockMode)
void lock(Object entity, LockModeType lockMode, Map<String, Object> properties)
<T> T find(Class<T> entityClass, Object primaryKey, LockModeType lockMode)
<T> T find(Class<T> entityClass, Object primaryKey, LockModeType
lockMode, Map<String, Object> properties)
void refresh(Object entity, LockModeType lockMode)
void refresh(Object entity, LockModeType lockMode, Map<String, Object> properties)
Помимо вышеуказанных методов, в Query API также есть методы для определения блокировок.
Предыдущий вопрос: 31. Для чего нужна аннотация @Transient?
Следующий вопрос: 33. Какие два вида кэшей (cache) вы знаете в JPA и для чего они нужны?
Все вопросы по теме: список
Все темы: список
Вопросы/замечания/предложения/нашли ошибку: напишите мне