Gymterview
middle

Какие уровни propagation существуют?

Propagation определяет, как метод с @Transactional ведёт себя относительно уже существующей транзакции.

Propagation Описание
REQUIRED (по умолчанию) Если транзакция есть – присоединиться. Если нет – создать новую
REQUIRES_NEW Всегда создавать новую транзакцию, текущую приостановить
SUPPORTS Если транзакция есть – присоединиться. Если нет – без транзакции
NOT_SUPPORTED Выполнять без транзакции, текущую приостановить
MANDATORY Обязательно должна быть транзакция, иначе – исключение
NEVER Обязательно НЕ должно быть транзакции, иначе – исключение
NESTED Если есть транзакция – создать вложенную (savepoint). Если нет – как REQUIRED
Пример: REQUIRED vs REQUIRES_NEW
@Service
public class OrderService {

    @Transactional // REQUIRED по умолчанию
    public void createOrder(Order order) {
        orderRepository.save(order);
        paymentService.processPayment(order);  // присоединится к той же транзакции
        auditService.logAction("ORDER_CREATED"); // REQUIRES_NEW -- своя транзакция
    }
}

@Service
public class PaymentService {
    @Transactional(propagation = Propagation.REQUIRED)
    public void processPayment(Order order) {
        // работает в той же транзакции, что и createOrder()
        // если здесь ошибка -- откатится ВСЯ транзакция, включая order
    }
}

@Service
public class AuditService {
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void logAction(String action) {
        // всегда в НОВОЙ транзакции
        // даже если основная транзакция откатится, лог сохранится
    }
}

NESTED – вложенные транзакции через savepoint

Пример
@Transactional
public void processBatch(List<Item> items) {
    for (Item item : items) {
        try {
            itemService.processItem(item); // NESTED
        } catch (Exception e) {
            // откатится только обработка этого item (savepoint)
        }
    }
}

На собеседовании: ключевое – разница между REQUIRED и REQUIRES_NEW. При REQUIRED ошибка внутреннего метода откатит всю внешнюю транзакцию. При REQUIRES_NEW – только внутреннюю. Частая ошибка – путать эти два типа.