Перейти к основному содержимому

Включение блокировки транзакций в Spring Data JPA

· 3 мин. чтения

1. Обзор

В этом кратком руководстве мы обсудим включение блокировки транзакций в Spring Data JPA для пользовательских методов запросов и предопределенных методов CRUD репозитория.

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

2. Типы блокировки

В JPA определены два основных типа блокировки: пессимистическая блокировка и оптимистичная блокировка.

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

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

2.2. Оптимистичная блокировка

В Optimistic Locking транзакция не блокирует объект немедленно. Вместо этого транзакция обычно сохраняет состояние объекта с присвоенным ему номером версии.

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

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

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

3. Включение блокировки транзакций для методов запроса

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

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

Чтобы указать блокировку для пользовательского метода запроса репозитория Spring Data JPA, мы можем аннотировать метод с помощью @Lock и указать требуемый тип режима блокировки:

@Lock(LockModeType.OPTIMISTIC_FORCE_INCREMENT)
@Query("SELECT c FROM Customer c WHERE c.orgId = ?1")
public List<Customer> fetchCustomersByOrgId(Long orgId);

Чтобы применить блокировку к предопределенным методам репозитория, таким как findAll или findById(id) , мы должны объявить метод в репозитории и аннотировать метод аннотацией Lock :

@Lock(LockModeType.PESSIMISTIC_READ)
public Optional<Customer> findById(Long customerId);

Когда блокировка включена явно и нет активной транзакции, базовая реализация JPA выдаст исключение TransactionRequiredException .

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

4. Установка времени ожидания блокировки транзакции

При использовании пессимистической блокировки база данных попытается немедленно заблокировать объект. Базовая реализация JPA выдает исключение LockTimeoutException , когда блокировка не может быть получена немедленно. Чтобы избежать таких исключений, мы можем указать значение тайм-аута блокировки.

В Spring Data JPA время ожидания блокировки можно указать с помощью аннотации QueryHints , поместив QueryHint в методы запроса:

@Lock(LockModeType.PESSIMISTIC_READ)
@QueryHints({@QueryHint(name = "javax.persistence.lock.timeout", value = "3000")})
public Optional<Customer> findById(Long customerId);

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

5. Вывод

В этом руководстве мы изучили различные типы режимов блокировки транзакций. Мы узнали, как включить блокировку транзакций в Spring Data JPA. Мы также рассмотрели настройку времени ожидания блокировки.

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

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

Конечно, примеры кода как для пессимистической блокировки, так и для оптимистичной блокировки можно найти на Github .