О чем статья?

  • Разберемся что такое транзакции и зачем они нужны
  • Детально рассмотрим аннотацию @Transactional и её свойства

C чего обычно всё начинается?

Разработчик пишет программу, использует базу, подключает источник данных будь-то REST или Kafka и о многопоточности и конкурентности мало кто думает. Нагрузка на неё возрастает и начинают замечать что данные в некоторых случаях перестают соответствовать действительности и требованиям. С возможными проблемами и путями их решения нам и предстоит ознакомиться.

JTA

Java Transaction API, более известное как JTA — это API для управления транзакциями в Java. Оно поддерживает открытие (start), завершение с сохранением (commit) и откат (rollback) транзакции ресурсонезависимым способом.

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

Транзакция

Транзакция (англ. transaction) — это группа последовательных операций с базой данных, которая представляет собой логическую единицу работы с данными. Транзакция может быть выполнена либо целиком и успешно, соблюдая целостность данных и независимо от параллельно идущих других транзакций, либо не выполнена вообще, и тогда она не должна произвести никакого эффекта. wikipedia.org

Требования к транзакциям

Перечислены в наборе ACID. Разберу лишь Iisolation, т.к. это требование как раз и относится к нашей теме.

Isolation

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

Уровни изолированности и феномены

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

Read Uncommited — грязное чтение (Dirty read)

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

Read Commited — неповторяющееся чтение и чтение фантомов

Параллельно выполняемые транзакции видят только зафиксированные изменения других транзакций. Защита от грязного чтения. Возникают:

  1. Неповторяющееся чтение — при повторном чтении в рамках одной транзакции, ранее прочитанные данные оказываются изменёнными.
Транзакция 1Транзакция 2
SELECT f2 FROM tbl1 WHERE f1=1;
UPDATE tbl1 SET f2=f2+3 WHERE f1=1;
COMMIT
SELECT f2 FROM tbl1 WHERE f1=1;
  1. Чтение фантомов — при повторном чтении одна и та же выборка даёт разные множества строк.
Транзакция 1Транзакция 2
SELECT SUM(f2) FROM tbl1;
INSERT INTO tbl1 (f1,f2) VALUES (15,20);
COMMIT
SELECT SUM(f2) FROM tbl1;

Repeatable Read

Читающая транзакция «не видит» изменения данных, которые были ею ранее прочитаны. Предотвращает феномен неповторяющегося чтения. Вставленные записи из другой транзакции всё ещё видны. (psql, mysql)

Serializable

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

Как работает аннотация @Transactional?

Spring динамически создает Proxy для классов, объявленных аннотацией @Transactional. Он позволяет накручивать дополнительные действия «до», «во время» и «после» вызовов методов обёрнутого объекта.

При оборачивании с использованием Spring AOP, прокси могут создаваться через JDK или с CGLIB. Развернутые ответы в этой статье.

Источники