Как работают транзакции в Spring
Что проверяют
Понимаете ли вы, что @Transactional это прокси, а не магия. Почти всегда следом идёт вопрос, почему аннотация не сработала.
Как отвечать
Spring оборачивает бин прокси-объектом. Прокси открывает транзакцию перед вызовом метода, коммитит после успешного выхода и откатывает при исключении. Сама транзакция живёт в TransactionSynchronizationManager, привязанная к потоку.
Ключевые настройки:
- propagation: как вести себя, если транзакция уже идёт.
REQUIREDпо умолчанию (присоединиться),REQUIRES_NEW(приостановить внешнюю и открыть свою),NESTED(точка сохранения внутри текущей); - isolation: уровень изоляции, от
READ_COMMITTEDдоSERIALIZABLE. По умолчанию берётся уровень базы; - readOnly: подсказка драйверу и Hibernate, позволяет пропустить грязную проверку сущностей;
- rollbackFor: по умолчанию откат идёт только на непроверяемых исключениях (
RuntimeExceptionиError). Проверяемое исключение транзакцию не откатывает, и это самый частый сюрприз.
Главное ограничение это самовызов. Вызов транзакционного метода изнутри того же класса идёт мимо прокси, и транзакции не будет вовсе:
public void outer() { this.inner(); } // @Transactional на inner не сработаетЛечится вынесением в отдельный бин или инъекцией самого себя.
Где спотыкаются
Не знают про самовызов и часами ищут, почему откат не происходит.
Ловят исключение внутри транзакционного метода и удивляются, что коммит прошёл: перехваченное исключение до прокси не доходит.
Ставят @Transactional на приватный метод, где прокси бессилен.
Держат в транзакции внешний HTTP-вызов и удерживают соединение с базой на время чужого таймаута.