Получить доступ

Как работает @Transactional и почему вызов метода внутри того же класса (self-invocation) может не открыть транзакцию?

Middle@TransactionalSpring
Ещё спрашивают так:
  • Почему @Transactional не работает при вызове метода из того же класса?
Коротко

@Transactional реализован прокси: вызов снаружи идёт через прокси, который открывает транзакцию и вызывает метод. Вызов this.other() внутри класса идёт мимо прокси — аннотация на other не срабатывает.

  • То же для @Async, @Cacheable, @Retryable и любых аспектов
  • Решения: вынести метод в другой бин; внедрить себя (@Lazy self); TransactionTemplate; AspectJ-weaving
  • Аннотация на private-методах не работает — прокси их не перехватывает

А ещё Транзакция внешнего метода, если она есть, всё равно действует на внутренний

Так подсказку видит пользователь sotto на живом собеседовании — за пару секунд, пока вы думаете вслух.

Подробно

Как работает. Для бина с @Transactional Spring создаёт прокси (CGLIB или JDK). Внешний вызов orderService.place() приходит в прокси; TransactionInterceptor берёт соединение, начинает транзакцию, вызывает настоящий метод, коммитит или откатывает по исключению.

Self-invocation. Внутри метода this — сам объект, а не прокси. Вызов this.saveAudit() с @Transactional(propagation = REQUIRES_NEW) выполнится в текущей транзакции (или без неё), настройки аннотации проигнорированы, ошибок не будет.

@Service
class OrderService {
    public void place(Order o) {
        save(o);            // мимо прокси — @Transactional на save не работает
    }
    @Transactional
    public void save(Order o) { ... }
}

Что делать:

  • вынести метод в отдельный бин (лучший вариант — часто это и отдельная ответственность);
  • программно: TransactionTemplate.execute(...);
  • внедрить собственный прокси (@Lazy @Autowired OrderService self) — работает, но запутывает;
  • AspectJ compile-time / load-time weaving — аспекты вплетаются в байткод.

Ещё ограничения прокси: не перехватываются private и final методы (CGLIB), а у JDK-прокси — методы не из интерфейса.

Что хотят услышать

  • Прокси как механизм работы @Transactional.
  • Почему внутренний вызов идёт мимо прокси.
  • Способы решения.
  • Другие ограничения прокси.

Частые ошибки

  • Ставить @Transactional на private-метод.
  • Ожидать REQUIRES_NEW при вызове из того же класса.

Что спросят дальше

  • Какие бывают propagation?
  • Чем отличается JDK-прокси от CGLIB?

Разбор темы

Связанные вопросы

Закрытая бета

Подсказки на собеседовании — в реальном времени

sotto слышит вопрос интервьюера и показывает короткий ответ и план, как в блоке «Коротко». Оставьте контакт — пришлём доступ и промокод.

  • Промокод на 2 часа — сверх пробных 30 минут
  • Программа для Windows — скачать сразу, войти — через Яндекс ID или VK ID
  • Прямая связь с разработчиком: что улучшить — сделаем