Ответ
Spring управляет вложенными транзакциями через механизм распространения транзакций (Propagation Behavior), определяемый аннотацией @Transactional. Ключевые аспекты:
Основные уровни распространения для вложенных вызовов:
REQUIRED(по умолчанию): Если транзакция существует, метод присоединится к ней. Если нет — создаст новую. Внутренний и внешний метод работают в одной физической транзакции.REQUIRES_NEW: Всегда создает новую, независимую транзакцию, приостанавливая существующую (если есть).NESTED: Создает вложенную точку сохранения (savepoint) внутри существующей транзакции. Откат внутренней операции не приводит к откату всей внешней транзакции.
Почему вызов внутри одного класса может не работать?
По умолчанию Spring AOP использует прокси на основе интерфейсов (или CGLIB). Вызов метода B() из метода A() того же класса происходит минуя прокси, поэтому аннотации @Transactional на B() игнорируются.
Решение:
-
Self-injection (Внедрение самого себя):
@Service public class MyService { @Autowired private MyService self; // Внедряем проксируемый бин @Transactional public void methodA() { // ... логика A self.methodB(); // Вызов через прокси -> транзакция работает } @Transactional(propagation = Propagation.REQUIRES_NEW) public void methodB() { // ... логика B в отдельной транзакции } } -
Использование
TransactionTemplateдля программного управления:@Service public class MyService { @Autowired private TransactionTemplate transactionTemplate; public void methodA() { // ... transactionTemplate.execute(status -> { return methodB(); // Выполняется в новой транзакции }); } } - Разделение логики на разные бины (рекомендуется).