Что происходит в Spring, когда один @Transactional метод вызывает другой @Transactional метод внутри того же сервиса?

«Что происходит в Spring, когда один @Transactional метод вызывает другой @Transactional метод внутри того же сервиса?» — вопрос из категории Spring, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Поведение определяется механизмом AOP (проксирование) и настройкой распространения (propagation) транзакции. По умолчанию (@Transactional(propagation = Propagation.REQUIRED)) внутренний метод не создает новую транзакцию, а присоединяется к существующей.

Проблема (по умолчанию): Поскольку вызов происходит внутри того же класса, он минует Spring AOP прокси. Это означает, что настройки @Transactional внутреннего метода игнорируются, и он выполняется в контексте транзакции внешнего метода.

Пример и решение:

@Service
public class OrderService {
    @Transactional
    public void placeOrder(Order order) { // Начинает транзакцию Tx1
        validate(order);
        updateInventory(order); // @Transactional НЕ СРАБОТАЕТ здесь!
        // Исключение здесь откатит всю Tx1
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void updateInventory(Order order) {
        // Этот метод выполнится БЕЗ НОВОЙ ТРАНЗАКЦИИ,
        // так как вызван напрямую, а не через прокси.
    }
}

Как добиться ожидаемого поведения:

  1. Вынести внутренний метод в отдельный сервис и инжектировать его.
  2. Использовать самовнедрение (self-injection) через @Autowired.
  3. Использовать TransactionTemplate для программного управления транзакциями.

Вывод: Важно понимать ограничения проксирования Spring, чтобы избежать ошибок в управлении транзакциями.