Sobes.tech
Назад к вопросам
Junior — Middle
66

Можете объяснить концепцию propagation level в Spring Data и её роль в управлении транзакциями?

Компании, где спрашивали
Prooftech
СБЕРСБЕР
IT OneIT One

Ответ от нейросети

sobes.tech AI

Что хотят услышать интервьюеры:

Propagation level в Spring — это правило, как метод должен вести себя относительно уже начатой транзакции. Оно определяет, использовать ли существующую транзакцию, создать новую, приостановить текущую или работать вообще без транзакции. Это один из основных механизмов, который помогает управлять границами транзакций в приложении.

Определение:

Propagation level — это стратегия распространения транзакционного контекста при входе в метод, помеченный @Transactional. Проще говоря, она отвечает на вопрос: если транзакция уже есть, что делать этому методу — присоединиться, начать новую, ничего не делать или выбросить ошибку.

В Spring чаще всего используются такие варианты:

  • REQUIRED — использовать существующую транзакцию или создать новую, если её нет;
  • REQUIRES_NEW — всегда создать новую, приостановив текущую;
  • SUPPORTS — работать в транзакции, если она есть, иначе без неё;
  • NOT_SUPPORTED — выполнять без транзакции, приостановив текущую;
  • MANDATORY — требовать уже существующую транзакцию;
  • NEVER — требовать отсутствие транзакции;
  • NESTED — создать вложенную транзакцию, если это поддерживает инфраструктура.

Пример использования:

Типичный случай — основной сервисный метод работает в одной транзакции, а логирование ошибки нужно сохранить отдельно, даже если основной процесс откатится.

@Service
public class OrderService {

    private final OrderRepository orderRepository;
    private final AuditService auditService;

    public OrderService(OrderRepository orderRepository, AuditService auditService) {
        this.orderRepository = orderRepository;
        this.auditService = auditService;
    }

    @Transactional
    public void createOrder(Order order) {
        orderRepository.save(order);

        try {
            // бизнес-операция
        } catch (Exception ex) {
            auditService.saveFailure(order.getId(), ex.getMessage());
            throw ex;
        }
    }
}

@Service
public class AuditService {

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void saveFailure(Long orderId, String message) {
        // запись аудита должна сохраниться независимо от отката createOrder()
    }
}

Пояснение кода:

createOrder() начинается в обычной транзакции REQUIRED по умолчанию. Все операции внутри него выполняются в одном транзакционном контексте и откатываются вместе при ошибке.
saveFailure() помечен REQUIRES_NEW, поэтому Spring приостанавливает текущую транзакцию, открывает новую и сохраняет запись аудита отдельно. Даже если внешняя транзакция будет откатена, запись аудита останется.
Если бы здесь использовался REQUIRED, то аудит вошёл бы в ту же транзакцию и откатился бы вместе с заказом.

Ключевые моменты:

  • Propagation level определяет, как метод ведёт себя при наличии уже открытой транзакции.
  • Это не про уровень изоляции, а про поведение границ транзакции.
  • REQUIRED — самый частый и обычно используется по умолчанию.
  • REQUIRES_NEW полезен для независимых операций, например аудита или outbox-сообщений.
  • Неправильный выбор propagation может привести к неожиданному откату или, наоборот, к лишнему разделению операций на несколько транзакций.
  • NESTED и некоторые режимы зависят от поддержки со стороны транзакционного менеджера и БД.