Можно ли использовать @Transactional на приватном методе и как это работает
Что проверяют
Знаете ли вы, что аннотация там не работает, и понимаете ли причину. Это тот же вопрос про прокси, только с другой стороны.
Как отвечать
Формально написать можно, но эффекта не будет: транзакция не откроется.
Причина в механизме. Spring кладёт в контейнер прокси вокруг вашего бина, и транзакцию открывает именно прокси при вызове метода снаружи. Приватный метод недоступен извне по определению, значит вызвать его через прокси невозможно: его зовут только изнутри класса, а внутренний вызов идёт напрямую, минуя обёртку.
То же самое касается final методов и final классов, если используется CGLIB: подкласс создать нельзя, перехватывать нечего. И тот же корень у известной ловушки самовызова: публичный метод, вызванный через this из соседнего метода того же класса, аннотацию тоже проигнорирует.
Что делать:
- сделать метод публичным и вызывать снаружи, обычно из другого бина;
- вынести его в отдельный бин и внедрить. Самый чистый вариант, потому что заодно разделяет ответственность;
- инжектировать себя и звать через эту ссылку, либо
AopContext.currentProxy()сexposeProxy = true. Работает, но выглядит как обход механизма; - перейти на AspectJ с вплетением в байт-код: тогда перехват не зависит от прокси и работает даже на приватных методах.
Отдельно стоит сказать, что Spring в режиме отладки предупреждает о транзакционных аннотациях на неподходящих методах, но молча их игнорирует в обычном режиме.
Где спотыкаются
Уверенно отвечают «да, работает».
Знают про ограничение, но не могут объяснить, почему так устроено.
Не связывают это с той же причиной, по которой не работает самовызов.
Забывают, что то же верно для @Cacheable, @Async и @PreAuthorize.