Что происходит при вызове метода с @Transactional из другого метода с @Transactional в том же классе
Что проверяют
Знаете ли вы про самовызов. Это самый частый практический вопрос про транзакции в Spring, и на нём отсеивается большинство.
Как отвечать
Ничего не происходит: аннотация на внутреннем методе не сработает вовсе. Вызов пойдёт напрямую, минуя прокси.
Причина в механизме. Spring кладёт в контейнер не сам объект, а прокси вокруг него, и транзакцию открывает именно прокси при вызове снаружи. Внутренний вызов это обычный this.method(), прокси в нём не участвует:
@Transactional public void outer() { this.inner(); }
@Transactional(propagation = REQUIRES_NEW) public void inner() { } // не применитсяЗначит REQUIRES_NEW не создаст новую транзакцию, а readOnly и свой timeout у внутреннего метода будут проигнорированы. Работа выполнится в транзакции внешнего метода, если она есть, и вообще без транзакции, если внешний метод не аннотирован.
Как чинят:
- вынести метод в отдельный бин и внедрить его. Самый чистый вариант;
- внедрить сам себя (
@Lazyссылка на свой бин) и звать через неё; - получить прокси через
AopContext.currentProxy(), для этого нуженexposeProxy = true; - перейти на AspectJ с вплетением в байт-код, там прокси не нужен вовсе.
Где спотыкаются
Уверенно отвечают «откроется вложенная транзакция».
Знают про самовызов, но не могут объяснить, почему так устроено.
Не помнят, что то же самое касается @Cacheable, @Async и @PreAuthorize: механизм общий.
Забывают, что аннотация на приватном методе бесполезна по той же причине.