Как управлять откатом транзакций при ошибке во вложенной транзакции в Spring?

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

Ответ

Поведение зависит от распространения транзакций (Propagation). Ключевые сценарии:

1. Вложенная транзакция с SAVEPOINT (Propagation.NESTED)

  • Внешняя транзакция приостанавливается, для вложенной создается точка сохранения (savepoint).
  • При ошибке во вложенной: откатывается только до savepoint, внешняя транзакция продолжает работу.
  • Поддерживается не всеми СУБД (например, работает в H2, но не в MySQL с InnoDB).

2. Независимая новая транзакция (Propagation.REQUIRES_NEW)

  • Внешняя транзакция приостанавливается, запускается полностью новая, независимая транзакция.
  • При ошибке во вложенной: откатывается только новая транзакция. Внешняя транзакция возобновляется и может быть зафиксирована или откатана отдельно.
    
    @Transactional
    public void outerMethod() {
    // Внешняя транзакция T1
    someRepository.save(entityA);
    try {
        innerMethod(); // Запускает T2, T1 приостановлена
    } catch (Exception e) {
        // T2 откатилась, T1 продолжается
    }
    // T1 может быть зафиксирована
    }

@Transactional(propagation = Propagation.REQUIRES_NEW) public void innerMethod() { // Новая независимая транзакция T2 someRepository.save(entityB); throw new RuntimeException("Rollback T2 only"); }



**3. Общая транзакция (Propagation.REQUIRED — по умолчанию)**
*   Метод выполняется в существующей внешней транзакции.
*   **Любая неперехваченная RuntimeException** приводит к откату **всей** единой транзакции.

**Вывод:** Используйте `REQUIRES_NEW` для изоляции критических операций, а `NESTED` — для частичных откатов, если СУБД поддерживает.