Рубрики
Без рубрики

Пессимистическая блокировка в JPA

Краткое руководство по использованию пессимистической блокировки в JPA

Автор оригинала: baeldung.

1. Обзор

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

Мы можем придумать два механизма контроля за эквивалентностью, которые позволяют нам это сделать: установить надлежащий уровень изоляции транзакций или установить блокировку данных, которые нам нужны на данный момент.

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

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

Мы можем использовать пессимистический замок, чтобы гарантировать, что никакие другие транзакции не могут изменять или удалять зарезервированные данные.

Есть два типа замков, которые мы можем сохранить: эксклюзивный замок и общий замок. Мы могли читать, но не писать в данных, когда кто-то другой держит общий замок. Для того, чтобы изменить или удалить зарезервированные данные, мы должны иметь эксклюзивный замок.

Мы можем приобрести эксклюзивные замки с помощью ‘ выбирать… ДЛЯ ОБНОВЛЕНИЯ ‘ заявления.

2. Режимы блокировки

Спецификация JPA определяет три пессимистических режима блокировки, которые мы собираемся обсудить:

  • PESSIMISTIC_READ – позволяет нам получить общий замок и предотвратить обновление или удаление данных
  • PESSIMISTIC_WRITE – позволяет нам получить эксклюзивную блокировку и предотвратить чтение, обновление или удаление данных
  • PESSIMISTIC_FORCE_INCREMENT – работает как PESSIMISTIC_WRITE и это дополнительно приращения атрибут версии версии сущности

Все они являются статическими членами LockModeType класса и позволяют транзакциям получить блокировку базы данных. Все они сохраняются до тех пор, пока транзакция не совершится или не откатится назад.

Стоит отметить, что мы можем получить только один замок за один раз. Если это невозможно НастойчивостьИсключаемость брошен.

2.1. PESSIMISTIC_READ

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

Иногда случается, что база данных, которую мы используем, не поддерживает PESSIMISTIC_READ блокировки, так что вполне возможно, что мы получаем PESSIMISTIC_WRITE блокировки вместо.

2.2. PESSIMISTIC_WRITE

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

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

2.3. PESSIMISTIC_FORCE_INCREMENT

Этот замок работает аналогично PESSIMISTIC_WRITE , но он был введен для сотрудничества с версиями лиц – лиц, которые имеют атрибут аннотирован @Version .

Любым обновлениям версий сущностей может предшествовать получение PESSIMISTIC_FORCE_INCREMENT замок. Приобретение этого замка приводит к обновлению столбца версии.

Это до сохранения поставщика, чтобы определить, является ли он поддерживает PESSIMISTIC_FORCE_INCREMENT для неверсионированных сущностей или нет. Если это не так, он бросает УпорствоЭксцепция .

2.4. Исключения

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

  • ПессимистическийЛоккоцепция – указывает на то, что получение блокировки или преобразование общего доступа в эксклюзивный замок не удается и приводит к откату на уровне транзакций
  • LockTimeoutException – указывает на то, что получение блокировки или преобразование общей блокировки в исключительное время и приводит к откату на уровне выписки
  • УпорствоЭксцепция – указывает на то, что возникла проблема сохранения. УпорствоЭксцепция и его подтипы, за исключением NoResultException , NonUniqueResultException, БлокировкаTimeoutException , и ЗапросTimeoutException, отмечает активную транзакцию, которая должна быть откатной.

3. Использование пессимистических замков

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

3.1. Найти

Это, наверное, самый простой способ. Этого достаточно, чтобы пройти LockModeType объект в качестве параметра к найти метод:

entityManager.find(Student.class, studentId, LockModeType.PESSIMISTIC_READ);

3.2. Запрос

Кроме того, мы можем использовать Запрос объект, а также и вызов setLockMode сеттер с режимом блокировки в качестве параметра:

Query query = entityManager.createQuery("from Student where studentId = :studentId");
query.setParameter("studentId", studentId);
query.setLockMode(LockModeType.PESSIMISTIC_WRITE);
query.getResultList()

3.3. Явная блокировка

Также можно вручную заблокировать результаты, полученные методом поиска:

Student resultStudent = entityManager.find(Student.class, studentId);
entityManager.lock(resultStudent, LockModeType.PESSIMISTIC_WRITE);

3.4. Обновление

Если мы хотим переписать состояние сущности по обновить метод, мы также можем установить блокировку:

Student resultStudent = entityManager.find(Student.class, studentId);
entityManager.refresh(resultStudent, LockModeType.PESSIMISTIC_FORCE_INCREMENT);

3.5. По имени Квери

@NamedQuery аннотация позволяет установить режим блокировки, а также:

@NamedQuery(name="lockStudent",
  query="SELECT s FROM Student s WHERE s.id LIKE :studentId",
  lockMode = PESSIMISTIC_READ)

4. Область блокировки

Параметр области блокировки определяет, как бороться с блокировкой отношений заблокированной сущности. Можно получить блокировку только на одной сущности, определенной в запросе, или дополнительно заблокировать ее отношения.

Для настройки области мы можем использовать ПессимистическийлокКоскоп enum. Он содержит два значения: НОРМАЛЬНЫЙ и РАСШИРЕННЫй .

Мы можем установить область, пройдя параметр ‘ Javax.persistance.lock.scope ‘ с ПессимистическийлокКоскоп значение в качестве аргумента к правильному методу EntityManager , Запрос , Типированный Квери или По имени Квери :

Map properties = new HashMap<>();
map.put("javax.persistence.lock.scope", PessimisticLockScope.EXTENDED);
    
entityManager.find(
  Student.class, 1L, LockModeType.PESSIMISTIC_WRITE, properties);

4.1. ПессимистическийЛокСкоп.NORMAL

Мы должны знать, что ПессимистическийLockScope.NORMAL является областью по умолчанию. С помощью этой области блокировки мы блокируем сам объект. При использовании с присоединившимся наследованием он также блокирует предков.

Рассмотрим пример кода с двумя сущностями:

@Entity
@Inheritance(strategy = InheritanceType.JOINED)
public class Person {

    @Id
    private Long id;
    private String name;
    private String lastName;

    // getters and setters
}

@Entity
public class Employee extends Person {

    private BigDecimal salary;

    // getters and setters
}

Когда мы хотим получить замок на Сотрудник , мы можем наблюдать за S’L запрос, который охватывает эти две сущности:

SELECT t0.ID, t0.DTYPE, t0.LASTNAME, t0.NAME, t1.ID, t1.SALARY 
FROM PERSON t0, EMPLOYEE t1 
WHERE ((t0.ID = ?) AND ((t1.ID = t0.ID) AND (t0.DTYPE = ?))) FOR UPDATE

4.2. ПессимистическийLockScope.EXTENDED

РАСШИРЕННЫй область охватывает ту же функциональность, что и НОРМАЛЬНЫЙ . Кроме того, он может блокировать связанные сущности в таблице присоединения .

Проще говоря, он работает с сущностями, аннотированными @ElementCollection или @OneToOne , @OneToMany и т.д. @JoinTable .

Давайте посмотрим на образец кода с @ElementCollection аннотация:

@Entity
public class Customer {

    @Id
    private Long customerId;
    private String name;
    private String lastName;
    @ElementCollection
    @CollectionTable(name = "customer_address")
    private List
addressList; // getters and setters } @Embeddable public class Address { private String country; private String city; // getters and setters }

Давайте проанализируем некоторые запросы при поиске Клиентская сущность:

SELECT CUSTOMERID, LASTNAME, NAME 
FROM CUSTOMER WHERE (CUSTOMERID = ?) FOR UPDATE

SELECT CITY, COUNTRY, Customer_CUSTOMERID 
FROM customer_address 
WHERE (Customer_CUSTOMERID = ?) FOR UPDATE

Мы видим, что есть два ‘ ДЛЯ ОБНОВЛЕНИЯ ‘ запросы, которые запирают строку в таблице клиента, а также строку в таблице присоединения.

Еще один интересный факт, который мы должны знать, заключается в том, что не все провайдеры настойчивости поддерживают области блокировки.

5. Установка тайм-аута блокировки

Помимо настройки областей блокировки, мы можем настроить другой параметр блокировки – тайм-аут. Значение тайм-аута — это количество миллисекунд, которое мы хотим ждать для получения блокировки до БлокировкаTimeoutException Возникает.

Мы можем изменить значение тайм-аута так же, как и области блокировки, используя Javax.persistence.lock.timeout’ с надлежащим количеством миллисекунд.

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

Map properties = new HashMap<>(); 
map.put("javax.persistence.lock.timeout", 1000L); 

entityManager.find(
  Student.class, 1L, LockModeType.PESSIMISTIC_READ, properties);

6. Заключение

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

Для этого мы можем выбирать между обсуждаемыми типами блокировок и, следовательно, изменять такие параметры, как их области или тайм-ауты.

С другой стороны, мы должны помнить, что понимание блокировок баз данных так же важно, как и понимание механизмов базовых систем баз данных. Важно также иметь в виду, что поведение пессимистических блокировок зависит от поставщика настойчивости, с которого мы работаем.

Наконец, исходный код этого учебника доступен на GitHub для спячка и для ЗатмениеЛинк .