1. Обзор
В этом кратком руководстве мы обсудим включение блокировок транзакций в Spring Data JPA для пользовательских методов запросов и предопределенных методов CRUD репозитория.
Мы также рассмотрим различные типы блокировок и установим таймауты блокировки транзакций.
2. Типы замков
В JPA определены два основных типа блокировок: Пессимистическая блокировка и Оптимистическая блокировка.
2.1 Пессимистическая блокировка
Когда мы используем Пессимистическую блокировку в транзакции и получаем доступ к сущности, она будет немедленно заблокирована . Транзакция освобождает блокировку либо путем фиксации, либо отката транзакции.
2.2 Оптимистическая блокировка
В Оптимистическая блокировка транзакция не блокирует объект немедленно. Вместо этого транзакция обычно сохраняет состояние сущности с присвоенным ей номером версии.
Когда мы пытаемся обновить состояние сущности в другой транзакции, транзакция сравнивает сохраненный номер версии с существующим номером версии во время обновления.
На этом этапе, если номер версии отличается, это означает, что объект не может быть изменен. Если существует активная транзакция, то эта транзакция будет откатана, и базовая реализация JPA вызовет исключение OptimisticLockException .
Помимо подхода с номером версии, мы можем использовать другие подходы, такие как временные метки, вычисление хэш-значения или сериализованная контрольная сумма, в зависимости от того, какой подход наиболее подходит для нашего текущего контекста разработки.
3. Включение блокировки транзакций для методов запроса
Чтобы получить блокировку объекта, мы можем аннотировать метод целевого запроса аннотацией Lock , передавая требуемый тип режима блокировки .
Типы режимов блокировки – это значения перечисления, которые должны быть указаны при блокировке объекта. Указанный режим блокировки затем передается в базу данных, чтобы применить соответствующую блокировку к объекту сущности.
Чтобы указать блокировку метода пользовательского запроса репозитория Spring Data JPA, мы можем аннотировать этот метод с помощью @Блокировка и укажите необходимый тип режима блокировки:
@Lock(LockModeType.OPTIMISTIC_FORCE_INCREMENT) @Query("SELECT c FROM Customer c WHERE c.orgId = ?1") public ListfetchCustomersByOrgId(Long orgId);
Чтобы принудительно заблокировать предопределенные методы репозитория , такие как findAll или findById(id) , мы должны объявить метод в репозитории и аннотировать метод с помощью аннотации Lock :
@Lock(LockModeType.PESSIMISTIC_READ) public OptionalfindById(Long customerId);
Когда блокировка явно включена и нет активной транзакции, базовая реализация JPA вызовет исключение TransactionRequiredException .
В случае, если блокировка не может быть предоставлена и конфликт блокировки не приводит к откату транзакции, JPA создает исключение LockTimeoutException . Но он не помечает активную транзакцию для отката.
4. Установка Таймаутов Блокировки Транзакций
При использовании пессимистической блокировки база данных попытается немедленно заблокировать объект. Базовая реализация JPA вызывает исключение LockTimeoutException , когда блокировка не может быть получена немедленно. Чтобы избежать таких исключений, мы можем указать значение тайм-аута блокировки.
В Spring Data JPA тайм-аут блокировки можно указать с помощью аннотации Queryhint , поместив подсказку Запроса в методы запроса:
@Lock(LockModeType.PESSIMISTIC_READ) @QueryHints({@QueryHint(name = "javax.persistence.lock.timeout", value = "3000")}) public OptionalfindById(Long customerId);
Более подробную информацию о настройке подсказки тайм-аута блокировки в различных областях можно найти в этой статье ObjectDB .
5. Заключение
В этом уроке мы изучили различные типы режимов блокировки транзакций. Мы узнали, как включить блокировку транзакций в Spring Data JPA. Мы также рассмотрели настройку таймаутов блокировки.
Применение правильных блокировок транзакций в нужных местах может помочь сохранить целостность данных в приложениях с большим объемом параллельного использования.
Когда транзакция должна строго придерживаться правил ACID, мы должны использовать пессимистическую блокировку. Оптимистическая блокировка должна применяться, когда нам нужно разрешить несколько одновременных чтений и когда возможная согласованность приемлема в контексте приложения.
Конечно, пример кода как для Пессимистической, так и для оптимистической блокировки можно найти на Github .