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

Весеннее управление транзакциями. Изоляция и распространение

Введение На мой взгляд, управление транзакциями – действительно важная тема для каждого bac… С тегами java, spring, программирование, разработка.

Вступление

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

Но я думаю, важно знать, как правильно использовать транзакции. Потому что иногда может случиться так, что внутри вашего метода возникло исключение, но транзакция не была откатана, и непонятно почему? Или какие-то другие “странные” случаи.

@Транзакционный

В spring есть аннотация @Transactional, которую можно использовать для переноса метода в транзакцию. Мы можем использовать его с интерфейсами (самый низкий приоритет), классами или определенными методами (самый высокий приоритет).

@Transactional применяется только для общедоступных методов, и метод должен вызываться извне компонента.

Аннотация @Transactional имеет несколько параметров. Я хотел бы сосредоточиться на двух из них: Изоляции и Распространении.

Изоляция является одним из основных свойств транзакций (Атомарность, Согласованность, Изоляция, Долговечность). Он описывает видимость изменений, применяемых параллельными транзакциями друг к другу.

В Spring можно установить одно из 5 значений уровня изоляции: DEFAULT, READ_UNCOMMITTED, READ_COMMITTED, REPETABLE_READ и SERIALIZABLE. Например,

@Transactional (isolation=Isolation.READ_COMMITED)

Каждый из этих уровней изоляции может иметь или не иметь различные побочные эффекты: “грязное” чтение, неповторяемое чтение и фантомное чтение. Что означает каждый из них?

“Грязное” чтение – одна транзакция может считывать изменения параллельной транзакции, которые еще не были зафиксированы.

Если вам нравится больше диаграмм, как мне:

Сначала начинается транзакция T1, затем мы запускаем транзакцию T2. После этого T1 изменяет строку с в базе данных, а T2 считывает ее. Происходит что-то неправильное, и T1 откатывается назад. И запись в T2 теперь грязная.

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

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

Давайте вернемся к уровням изоляции и проверим их возможные побочные эффекты.

  1. Значение ПО УМОЛЧАНИЮ используется, когда мы хотим использовать уровень изоляции по умолчанию нашей СУБД. Значение по умолчанию для PostgreSQL, Oracle и SQL server – READ_COMMITTED, для MySQL – REPEATABLE_READ.

  2. с уровнем изоляции READ_UNCOMMITTED мы можем иметь все три побочных эффекта

  3. Уровень изоляции READ_COMMITTED имеет одно изменение по сравнению с READ_UNCOMMITTED – он предотвращает “грязное” чтение.

  4. REPEATABLE_READ предотвращает “грязное” и неповторяемое чтение.

  5. СЕРИАЛИЗУЕМЫЙ уровень изоляции предотвращает все упомянутые выше побочные эффекты. Он выполняет все транзакции последовательно.

возможный возможный READ_UNCOMMITTED возможный
это невозможно возможный READ_COMMITTED возможный
это невозможно возможный ПОВТОРЯЕМОЕ_ЧИТАНИЕ это невозможно
это невозможно это невозможно СЕРИАЛИЗУЕМЫЙ это невозможно

Существует также еще один важный параметр @Transactional: распространение. Для распространения может быть установлено значение REQUIRED, REQUIRES_NEW, ОБЯЗАТЕЛЬНОЕ, SUPPORTS, NOT_SUPPORTED, ВЛОЖЕННОЕ или НИКОГДА. Пример:

@Transactional (propagation=Propagation.REQUIRED)
  1. ТРЕБУЕМЫЙ уровень распространения использует существующую транзакцию, если таковая имеется. В противном случае создается новая транзакция.

  2. Уровень распространения REQUIRES_NEW говорит о приостановке внешней транзакции (если она есть) и создании новой.

  3. ОБЯЗАТЕЛЬНОЕ распространение использует существующую транзакцию, если таковая имеется. В противном случае будет выдано исключение.

  4. ПОДДЕРЖИВАЕТ уровень распространения, использующий текущую транзакцию, если таковая имеется. В противном случае новый не создается.

  5. NOT_SUPPORTED приостанавливает текущую транзакцию, если таковая имеется.

  6. ВЛОЖЕННАЯ создает вложенную транзакцию, когда уже существует существующая транзакция, или работает как ТРЕБУЕТСЯ, если транзакции нет.

  7. НИКОГДА не генерирует исключение, если есть активная транзакция.

требуемый T2 Нет
требуемый T1 T1
ТРЕБУЕТСЯ НОВОЕ T2 Нет
ТРЕБУЕТСЯ НОВОЕ T2 T1
обязательный Исключение Нет
обязательный T1 T1
НЕ ПОДДЕРЖИВАЕТСЯ Нет Нет
НЕ ПОДДЕРЖИВАЕТСЯ Нет T1
поддерживает Нет Нет
поддерживает T1 T1
никогда Нет Нет
никогда Исключение T1
вложенный T2 Нет
вложенный T2 T1

Также интересно, что Spring framework выполняет автоматический откат транзакций только для непроверенных (во время выполнения) исключений. Чтобы изменить его, мы можем использовать откат для параметра. Например, мы можем поместить

@Transactional (rollbackFor=Exception.class)

Вывод

В статье я попытался описать, как использовать Isolation и Распространение параметров @Transactional весной.

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

Оригинал: “https://dev.to/ildar10/spring-transaction-management-isolation-and-propagation-483m”