← Назад к вопросам

Что такое Transaction Propagation в Spring?

3.0 Senior🔥 181 комментариев
#Spring Framework#Базы данных и SQL

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Transaction Propagation в Spring

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

Основная проблема

@Service
public class OrderService {
    @Transactional
    public void createOrder(Order order) {
        // Логика создания заказа
        orderRepository.save(order);
        emailService.sendConfirmation(order);  // Вложенный вызов
    }
}

@Service
public class EmailService {
    @Transactional
    public void sendConfirmation(Order order) {
        // Отправка email
    }
}

Вопрос: должны ли оба метода работать в одной транзакции или в отдельных? Это определяет Transaction Propagation.

Типы Propagation

1. REQUIRED (по умолчанию)

Если существует транзакция, использовать её. Если нет — создать новую.

@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
    methodB();  // Будет использовать ту же транзакцию
}

@Transactional(propagation = Propagation.REQUIRED)
public void methodB() {
    // Работает в той же транзакции
}

Поведение:

  • Если methodA() уже в транзакции, methodB() использует её
  • Если methodA() не в транзакции, для methodB() создаётся новая

Это самый частый выбор для большинства случаев.

2. REQUIRES_NEW

Всегда создавать новую транзакцию, приостанавливая текущую.

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void criticalOperation() {
    // Работает в НОВОЙ транзакции
    // Даже если вызвана из другого транзакционного метода
}

@Transactional
public void methodA() {
    try {
        criticalOperation();  // Создаёт новую транзакцию
    } catch (Exception e) {
        // criticalOperation() успела закоммититься или откатиться независимо
    }
}

Диаграмма:

methodA транзакция: [========   SUSPENDED   ========]
criticalOperation:        [=====REQUIRES_NEW=====]

Это полезно, когда нужна независимая транзакция, которая должна завершиться независимо от внешней.

3. NESTED

Создаёт вложенную транзакцию (savepoint), если поддерживается БД.

@Transactional
public void methodA() {
    // Точка сохранения 1
    methodB();  // Может откатиться до точки сохранения
}

@Transactional(propagation = Propagation.NESTED)
public void methodB() {
    // Работает в savepoint
}

Диаграмма:

methodA:    [===============================]
methodB:               [===NESTED===]
           SP1        SP2         Commit/Rollback

Если methodB() провалится, только её работа откатится, но methodA() продолжит работу.

4. SUPPORTS

Использовать транзакцию, если она существует, иначе работать без неё.

@Transactional(propagation = Propagation.SUPPORTS)
public void readOnlyOperation() {
    // Может работать в транзакции или без неё
    return getUserData();
}

Это полезно для read-only методов, которые могут быть вызваны как из транзакционных, так и из не-транзакционных методов.

5. NOT_SUPPORTED

Работать БЕЗ транзакции, приостанавливая текущую если она есть.

@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void nonTransactionalOperation() {
    // Работает БЕЗ транзакции, даже если вызвана из @Transactional метода
}

@Transactional
public void methodA() {
    nonTransactionalOperation();  // Текущая транзакция приостанавливается
}

6. MANDATORY

Требует существующую транзакцию. Если её нет — выбросить исключение.

@Transactional(propagation = Propagation.MANDATORY)
public void operationRequiresTransaction() {
    // Выбросит TransactionRequiredException, если нет активной транзакции
}

7. NEVER

Не должна быть в транзакции. Если транзакция существует — выбросить исключение.

@Transactional(propagation = Propagation.NEVER)
public void operationForbidddenTransaction() {
    // Выбросит IllegalTransactionStateException, если есть активная транзакция
}

Практические примеры

Пример 1: Логирование независимо от основной транзакции

@Service
public class OrderService {
    @Autowired
    private OrderRepository orderRepository;
    
    @Autowired
    private AuditService auditService;
    
    @Transactional
    public void createOrder(Order order) {
        orderRepository.save(order);
        // Логирование выполнится независимо
        auditService.logAction("Order created: " + order.getId());
    }
}

@Service
public class AuditService {
    @Autowired
    private AuditRepository auditRepository;
    
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void logAction(String message) {
        // Запись в лог БД в отдельной транзакции
        // Даже если createOrder() откатится, лог останется
        auditRepository.save(new AuditLog(message));
    }
}

Пример 2: Обработка ошибок с вложенной транзакцией

@Service
public class PaymentService {
    @Transactional
    public void processPayment(Payment payment) {
        paymentRepository.save(payment);
        
        try {
            notifyCustomer(payment);
        } catch (Exception e) {
            // Ошибка уведомления не откатит платёж
        }
    }
    
    @Transactional(propagation = Propagation.NESTED)
    public void notifyCustomer(Payment payment) {
        emailService.sendConfirmation(payment);
    }
}

Пример 3: Read-only операции

@Service
public class UserService {
    @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
    public User getUser(Long id) {
        // Может работать в транзакции для консистентности
        // или без неё для оптимизации
        return userRepository.findById(id);
    }
    
    @Transactional
    public void updateUser(User user) {
        getUser(user.getId());  // Использует транзакцию от updateUser
        userRepository.save(user);
    }
}

Таблица всех типов Propagation

ТипТекущая транзакцияНово тканьПоведение
REQUIREDEXISTSReuseИспользует существующую
REQUIREDNONECreateСоздаёт новую
REQUIRES_NEWEXISTSSuspend+CreateПриостанавливает и создаёт
REQUIRES_NEWNONECreateСоздаёт новую
NESTEDEXISTSSavepointСоздаёт savepoint
NESTEDNONECreateСоздаёт новую
SUPPORTSEXISTSReuseИспользует существующую
SUPPORTSNONENoneРаботает без транзакции
NOT_SUPPORTEDEXISTSSuspendПриостанавливает текущую
NOT_SUPPORTEDNONENoneРаботает без транзакции
MANDATORYEXISTSReuseИспользует существующую
MANDATORYNONEErrorВыбрасывает исключение
NEVEREXISTSErrorВыбрасывает исключение
NEVERNONENoneРаботает без транзакции

Рекомендации

Используйте REQUIRED (по умолчанию) для большинства операций — это предсказуемо и просто.

Используйте REQUIRES_NEW для независимых операций типа логирования, аудита или отправки уведомлений.

Используйте NESTED когда поддерживается БД (PostgreSQL, Oracle) и нужна частичная откат.

Используйте SUPPORTS для read-only операций.

Избегайте MANDATORY и NEVER в обычном коде — они усложняют отладку.

Transaction Propagation — мощный механизм для контроля границ транзакций в сложных приложениях.

Что такое Transaction Propagation в Spring? | PrepBro