О чем статья?
- Разберемся что такое транзакции и зачем они нужны
- Детально рассмотрим аннотацию
@Transactionalи её свойства
C чего обычно всё начинается?
Разработчик пишет программу, использует базу, подключает источник данных будь-то REST или Kafka и о многопоточности и конкурентности мало кто думает. Нагрузка на неё возрастает и начинают замечать что данные в некоторых случаях перестают соответствовать действительности и требованиям. С возможными проблемами и путями их решения нам и предстоит ознакомиться.
JTA
Java Transaction API, более известное как JTA — это API для управления транзакциями в Java. Оно поддерживает открытие (start), завершение с сохранением (commit) и откат (rollback) транзакции ресурсонезависимым способом.
Истинная сила JTA заключается в его способности управлять несколькими ресурсами (например, базами данных, службами обмена сообщениями) в рамках одной транзакции.
Транзакция
Транзакция (англ. transaction) — это группа последовательных операций с базой данных, которая представляет собой логическую единицу работы с данными. Транзакция может быть выполнена либо целиком и успешно, соблюдая целостность данных и независимо от параллельно идущих других транзакций, либо не выполнена вообще, и тогда она не должна произвести никакого эффекта. wikipedia.org
Требования к транзакциям
Перечислены в наборе ACID. Разберу лишь I — isolation, т.к. это требование как раз и относится к нашей теме.
Isolation
Во время выполнения транзакции параллельные транзакции не должны оказывать влияния на её результат. Полная изолированность — требование дорогое, поэтому существуют уровни изолированности, определяющие в какой мере допускается получение несогласованных данных.
Уровни изолированности и феномены
Каждый последующий уровень предназначен для решения проблемы текущей ступени. Всего выделяют четыре уровня.
Read Uncommited — грязное чтение (Dirty read)
Уровень с самой плохой согласованностью данных, но самой высокой скоростью. Каждая транзакция видит незафиксированные изменения другой транзакции, которая впоследствии может откатиться.
Read Commited — неповторяющееся чтение и чтение фантомов
Параллельно выполняемые транзакции видят только зафиксированные изменения других транзакций. Защита от грязного чтения. Возникают:
- Неповторяющееся чтение — при повторном чтении в рамках одной транзакции, ранее прочитанные данные оказываются изменёнными.
| Транзакция 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 | Транзакция 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. Развернутые ответы в этой статье.
Источники
- Guide to Jakarta EE JTA — baeldung.com
- Транзакции в Spring Framework — Kirill Sereda на medium.com
- Data Access — Spring Framework Documentation
- Spring AOP. Маленький вопросик с собеседования — habr.com
- @Transactional в Spring под капотом — habr.com
- Уровни изолированности транзакций для самых маленьких — habr.com
- Уровень изолированности транзакций — wikipedia.org