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

Какие знаешь атрибуты Propagation в @Transactional?

3.0 Senior🔥 91 комментариев
#Spring Framework

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

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

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

Propagation в @Transactional: все типы и примеры

Propagation - это атрибут аннотации @Transactional в Spring, который определяет как должна вести себя транзакция когда вызывается метод в другом транзакционном методе. Это критически важно для правильного управления транзакциями.

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

Самый частый тип - если транзакция уже существует, использовать её; если нет, создать новую:

@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;
    
    @Autowired
    private AuditService auditService;
    
    // REQUIRED - использует текущую транзакцию или создаёт новую
    @Transactional(propagation = Propagation.REQUIRED)
    public User createUser(String email, String name) {
        User user = new User(email, name);
        User savedUser = userRepository.save(user);
        
        // Эта строка выполняется в ОДНОЙ транзакции с createUser
        auditService.logUserCreation(savedUser);
        
        return savedUser;
    }
}

@Service
public class AuditService {
    @Autowired
    private AuditLogRepository auditLogRepository;
    
    // REQUIRED - присоединяется к транзакции из UserService
    @Transactional(propagation = Propagation.REQUIRED)
    public void logUserCreation(User user) {
        AuditLog log = new AuditLog();
        log.setAction("USER_CREATED");
        log.setUserId(user.getId());
        auditLogRepository.save(log);
    }
}

Поведение: Если что-то пойдёт не так в logUserCreation(), вся транзакция (включая создание пользователя) откатится.

2. REQUIRES_NEW

Создаёт НОВУЮ транзакцию, даже если уже существует. Текущая транзакция приостанавливается:

@Service
public class OrderService {
    @Autowired
    private OrderRepository orderRepository;
    
    @Autowired
    private NotificationService notificationService;
    
    @Transactional(propagation = Propagation.REQUIRED)
    public Order createOrder(OrderRequest request) {
        Order order = new Order(request);
        Order savedOrder = orderRepository.save(order);
        
        // REQUIRES_NEW - создаёт новую транзакцию
        try {
            notificationService.sendConfirmationEmail(savedOrder);
        } catch (Exception e) {
            // Даже если отправка письма упадёт, заказ будет сохранён
            logger.warn("Failed to send confirmation email", e);
        }
        
        return savedOrder;
    }
}

@Service
public class NotificationService {
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void sendConfirmationEmail(Order order) {
        // Эта транзакция НЕ зависит от транзакции OrderService
        // Если она упадёт - заказ всё равно сохранится
        EmailLog emailLog = new EmailLog();
        emailLog.setOrderId(order.getId());
        emailLogRepository.save(emailLog);
        
        emailService.send(order.getCustomerEmail());
    }
}

Использование: Когда некоторые операции должны быть независимыми (логирование, уведомления, аналитика).

3. SUPPORTS

Если транзакция существует, использовать её; если нет, выполнить без транзакции:

@Service
public class UserQueryService {
    @Autowired
    private UserRepository userRepository;
    
    // SUPPORTS - использует транзакцию если она уже есть
    @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
    public User getUserById(UUID id) {
        return userRepository.findById(id).orElse(null);
    }
    
    // Сценарий 1: Вызов из @Transactional метода
    @Transactional
    public void processUser(UUID userId) {
        User user = getUserById(userId);  // Использует текущую транзакцию
    }
    
    // Сценарий 2: Вызов из обычного метода
    public User getPublicUser(UUID userId) {
        return getUserById(userId);  // Выполняется БЕЗ транзакции
    }
}

4. MANDATORY

Метод ТРЕБУЕТ наличие активной транзакции. Если её нет - выбросит исключение:

@Service
public class PaymentService {
    @Autowired
    private PaymentRepository paymentRepository;
    
    // MANDATORY - требует транзакцию
    @Transactional(propagation = Propagation.MANDATORY)
    public void recordPayment(Payment payment) {
        paymentRepository.save(payment);
    }
    
    // Правильно - вызов из @Transactional метода
    @Transactional
    public void processOrder(Order order) {
        Payment payment = new Payment(order);
        recordPayment(payment);  // OK - транзакция существует
    }
    
    // Неправильно - выбросит InvalidTransactionStateException
    public void asyncPayment(Payment payment) {
        recordPayment(payment);  // ERROR - нет транзакции!
    }
}

5. NOT_SUPPORTED

Если транзакция существует, она будет приостановлена и метод выполнится БЕЗ транзакции:

@Service
public class AnalyticsService {
    @Autowired
    private AnalyticsRepository analyticsRepository;
    
    // NOT_SUPPORTED - выполняется БЕЗ транзакции
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public void logAnalyticsEvent(String eventName) {
        // Это записывается напрямую в БД, не в транзакции
        // Так что даже если основная транзакция откатится - событие будет записано
        AnalyticsEvent event = new AnalyticsEvent(eventName);
        analyticsRepository.save(event);
    }
    
    @Transactional
    public void processPayment(Order order) {
        // Основная логика в транзакции
        updateOrderStatus(order);
        
        // Логирование выполняется БЕЗ транзакции
        logAnalyticsEvent("PAYMENT_PROCESSED");
    }
}

6. NEVER

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

@Service
public class BackgroundTaskService {
    @Autowired
    private TaskRepository taskRepository;
    
    // NEVER - НЕ должна быть транзакция
    @Transactional(propagation = Propagation.NEVER)
    public void executeBackgroundTask(Task task) {
        taskRepository.save(task);
    }
    
    // Правильно - вызов БЕЗ транзакции
    public void scheduleTask(Task task) {
        executeBackgroundTask(task);  // OK
    }
    
    // Неправильно - выбросит IllegalTransactionStateException
    @Transactional
    public void invalidCall(Task task) {
        executeBackgroundTask(task);  // ERROR!
    }
}

7. NESTED

Использует savepoints для вложенных транзакций (только с определёнными БД):

@Service
public class NestedTransactionService {
    @Autowired
    private OrderRepository orderRepository;
    
    @Autowired
    private ItemService itemService;
    
    @Transactional(propagation = Propagation.REQUIRED)
    public Order createOrderWithItems(OrderRequest request) {
        Order order = new Order(request);
        orderRepository.save(order);
        
        for (ItemRequest itemRequest : request.getItems()) {
            try {
                itemService.addItem(order, itemRequest);
            } catch (Exception e) {
                logger.warn("Failed to add item, continuing", e);
                // Ошибка в одном item не отменяет весь заказ
            }
        }
        
        return order;
    }
    
    @Transactional(propagation = Propagation.NESTED)
    public void addItem(Order order, ItemRequest itemRequest) {
        Item item = new Item(order, itemRequest);
        itemRepository.save(item);
    }
}

Таблица сравнения Propagation типов

ТипНовая ТХЕсли ТХ существуетИспользование
REQUIREDДаИспользует существующуюDefault, большинство случаев
REQUIRES_NEWДаСоздаёт новуюНезависимые операции
SUPPORTSНетИспользует если естьRead-only операции
MANDATORY-Требует наличиеКритичные операции
NOT_SUPPORTEDНетПриостанавливаетЛогирование, аналитика
NEVERНетВыбросит ошибкуФоновые задачи
NESTEDДаSavepointВложенные ТХ

Best Practices

REQUIRED для основной бизнес-логики ✓ REQUIRES_NEW для независимых операций (логирование, уведомления) ✓ SUPPORTS с readOnly=true для read-only методов ✓ MANDATORY для критичного кода, где ошибка без транзакции - это ошибка архитектуры ✓ NOT_SUPPORTED для аналитики, которая должна работать независимо ✓ Всегда устанавливайте readOnly=true для методов, которые только читают ✓ Используйте REQUIRES_NEW для операций, которые должны успешно завершиться даже если основная транзакция упадёт ✓ Помните, что NESTED работает не на всех БД (SQLServer, Oracle да, MySQL нет)

Правильный выбор Propagation - это ключ к надёжной и предсказуемой работе транзакций в приложении.