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

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

Руководство по пониманию оптимистической блокировки в JPA, а также ее вариантов использования.

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

1. введение

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

Более того, мы должны убедиться, что данные остаются согласованными между параллельными чтениями и обновлениями.

Для достижения этой цели мы можем использовать оптимистичный механизм блокировки, предоставляемый Java Persistence API. Это приводит к тому, что несколько обновлений, выполненных на одних и тех же данных одновременно, не мешают друг другу.

2. Понимание Оптимистической Блокировки

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

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

Если значение за это время изменилось, возникает исключение OptimisticLockException . В противном случае транзакция фиксирует обновление и увеличивает значение свойства версии.

3. Пессимистическая блокировка против Оптимистичная Блокировка

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

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

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

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

Напротив, пессимистический механизм блокировки включает в себя блокировку объектов на уровне базы данных.

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

4. Атрибуты версии

Атрибуты версии-это свойства с аннотацией @Version . Они необходимы для включения оптимистической блокировки. Давайте посмотрим пример класса сущностей:

@Entity
public class Student {

    @Id
    private Long id;

    private String name;

    private String lastName;

    @Version
    private Integer version;

    // getters and setters

}

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

  • каждый класс сущностей должен иметь только один атрибут версии
  • он должен быть помещен в основную таблицу для сущности, сопоставленной с несколькими таблицами
  • тип атрибута версии должен быть одним из следующих: int , Integer , long , Long , short , |/Short , java.sql.Timestamp

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

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

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

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

JPA предоставляет нам два различных оптимистических режима блокировки (и два псевдонима):

  • ОПТИМИСТИЧНЫЙ – он получает оптимистическую блокировку чтения для всех сущностей, содержащих атрибут версии
  • OPTIMISTIC_FORCE_INCREMENT – он получает оптимистическую блокировку, такую же, как OPTIMISTIC , и дополнительно увеличивает значение атрибута версии
  • ЧИТАТЬ – это синоним ОПТИМИСТИЧНЫЙ
  • НАПИШИТЕ – это синоним OPTIMISTIC_FORCE_INCREMENT

Мы можем найти все типы, перечисленные выше, в классе LockModeType .

5.1. ОПТИМИСТИЧНЫЙ (ЧИТАТЬ)

Как мы уже знаем, режимы ОПТИМИСТИЧЕСКИЙ и ЧТЕНИЕ блокировка являются синонимами. Однако спецификация JPA рекомендует нам использовать ОПТИМИСТИЧНЫЙ в новых приложениях.

Всякий раз, когда мы запрашиваем ОПТИМИСТИЧНЫЙ режим блокировки, поставщик персистентности будет предотвращать грязные чтения наших данных, а также неповторяемые чтения .

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

  • обновлено или удалено, но не зафиксировано
  • за это время он успешно обновил или удалил

5.2. OPTIMISTIC_INCREMENT (ЗАПИСЬ)

Как и ранее, OPTIMISTIC_INCREMENT и WRITE являются синонимами, но первое предпочтительнее.

OPTIMISTIC_INCREMENT должен соответствовать тем же условиям, что и ОПТИМИСТИЧЕСКИЙ режим блокировки. Кроме того, он увеличивает значение атрибута версии. Однако не указано, следует ли это делать немедленно или может быть отложено до фиксации или сброса.

Стоит знать, что поставщику персистентности разрешено предоставлять функциональность OPTIMISTIC_INCREMENT , когда запрашивается режим OPTIMISTIC lock.

6. Использование Оптимистической Блокировки

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

6.1. Найти

Чтобы запросить оптимистическую блокировку, мы можем передать правильный LockModeType в качестве аргумента для поиска метода EntityManager :

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

6.2. Запрос

Другой способ включить блокировку-использовать метод setLockMode объекта Query :

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

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

Мы можем установить блокировку, вызвав метод Entitymanager lock :

Student student = entityManager.find(Student.class, id);
entityManager.lock(student, LockModeType.OPTIMISTIC);

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

Мы можем вызвать метод refresh так же, как и предыдущий метод:

Student student = entityManager.find(Student.class, id);
entityManager.refresh(student, LockModeType.READ);

6.5. NamedQuery

Последний вариант-использовать @NamedQuery со свойством Режим блокировки :

@NamedQuery(name="optimisticLock",
  query="SELECT s FROM Student s WHERE s.id LIKE :id",
  lockMode = WRITE)

7. Исключение OptimisticLockException

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

Хорошо знать, как мы можем реагировать на OptimisticLockException . Удобно, что это исключение содержит ссылку на конфликтующую сущность. Однако поставщик персистентности не обязан предоставлять его в каждой ситуации . Нет никакой гарантии, что объект будет доступен.

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

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

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

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

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

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

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