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

Что такое уровни propagation в транзакциях?

2.0 Middle🔥 191 комментариев
#SOLID и паттерны проектирования#Spring Boot и Spring Data

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

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

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

Уровни propagation в транзакциях

Propagation (распространение) в транзакциях — это механизм в Spring Framework, который определяет, как транзакция должна вести себя, когда метод с аннотацией @Transactional вызывает другой метод, также помеченный как @Transactional. Уровень propagation управляет созданием новой транзакции, использованием существующей или отсутствием транзакции.

Основные концепции

Propagation определяет:

  • Создавать ли новую транзакцию при вызове метода
  • Использовать ли существующую транзакцию
  • Что делать, если транзакция не существует
  • Поведение при ошибках

Основные уровни Propagation

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

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.annotation.Propagation;

@Service
public class OrderService {
    
    @Transactional(propagation = Propagation.REQUIRED)
    public void processOrder(Long orderId) {
        // Если транзакция существует - используем её
        // Если нет - создаём новую
        Order order = findOrder(orderId);
        order.setStatus("PROCESSING");
        save(order);
        
        // Вызов другого метода в той же транзакции
        applyDiscount(orderId);
    }
    
    @Transactional(propagation = Propagation.REQUIRED)
    public void applyDiscount(Long orderId) {
        // Будет выполняться в ТОЙ ЖЕ транзакции
        Order order = findOrder(orderId);
        order.setDiscount(10);
        save(order);
    }
}

// Использование
@Service
public class Main {
    private OrderService orderService;
    
    @Transactional
    public void createOrder() {
        // processOrder выполняется в существующей транзакции
        // applyDiscount также в той же транзакции
        orderService.processOrder(1L);
    }
}

2. REQUIRES_NEW

@Service
public class OrderService {
    
    @Transactional(propagation = Propagation.REQUIRED)
    public void processOrder(Long orderId) {
        Order order = findOrder(orderId);
        order.setStatus("PROCESSING");
        save(order);
        
        // Вызывает метод в НОВОЙ транзакции
        logOrderProcessing(orderId);
    }
    
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void logOrderProcessing(Long orderId) {
        // Создаёт новую транзакцию, независимую от родительской
        // Если родительская откатится - логирование останется
        OrderLog log = new OrderLog();
        log.setOrderId(orderId);
        log.setTimestamp(System.currentTimeMillis());
        log.setStatus("LOGGED");
        saveLog(log);
    }
}

// Пример с ошибками
@Service
public class ProcessingService {
    @Transactional(propagation = Propagation.REQUIRED)
    public void process() {
        // Некая обработка
        doSomething();
        
        // Логирование в отдельной транзакции
        // Даже если process() откатится - лог будет сохранён
        logProgress();
        
        throw new RuntimeException("Ошибка обработки");
    }
    
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void logProgress() {
        // Выполняется в отдельной транзакции
        // Не откатывается при ошибке в process()
        Log log = new Log("Progress logged");
        logRepository.save(log);
    }
}

3. SUPPORTS

@Service
public class ReadService {
    
    @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
    public Order getOrder(Long orderId) {
        // Если есть активная транзакция - используем её
        // Если нет - выполняем БЕЗ транзакции
        return orderRepository.findById(orderId).orElse(null);
    }
    
    @Transactional(propagation = Propagation.SUPPORTS)
    public String getOrderDetails(Long orderId) {
        // Оптимизирует для операций, которые могут работать
        // и в транзакции, и без неё
        Order order = getOrder(orderId);
        return order != null ? order.toString() : "Not found";
    }
}

4. NOT_SUPPORTED

@Service
public class AuditService {
    
    @Transactional(propagation = Propagation.REQUIRED)
    public void updateUser(User user) {
        user.setUpdatedAt(System.currentTimeMillis());
        userRepository.save(user);
        
        // Аудит выполняется БЕЗ транзакции
        // Даже если updateUser откатится - аудит будет сохранён
        auditAction(user.getId(), "USER_UPDATED");
    }
    
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public void auditAction(Long userId, String action) {
        // Выполняется БЕЗ транзакции
        // Автоматически коммитится каждый раз
        AuditLog log = new AuditLog();
        log.setUserId(userId);
        log.setAction(action);
        log.setTimestamp(new Date());
        auditRepository.save(log);
    }
}

5. MANDATORY

@Service
public class PaymentService {
    
    @Transactional(propagation = Propagation.REQUIRED)
    public void processPayment(Payment payment) {
        // Обработка платежа в транзакции
        payment.setStatus("PROCESSING");
        paymentRepository.save(payment);
        
        // MANDATORY требует существования транзакции
        updateUserBalance(payment.getUserId(), payment.getAmount());
    }
    
    @Transactional(propagation = Propagation.MANDATORY)
    public void updateUserBalance(Long userId, BigDecimal amount) {
        // ОБЯЗАТЕЛЬНО должна быть активная транзакция
        // Если вызвать без транзакции - выбросит исключение
        User user = userRepository.findById(userId).orElseThrow();
        user.setBalance(user.getBalance().add(amount));
        userRepository.save(user);
    }
}

// Ошибка при вызове БЕЗ транзакции
@Service
public class BadExample {
    @Autowired
    private PaymentService paymentService;
    
    public void callWithoutTransaction() {
        // Это выбросит IllegalTransactionStateException
        // paymentService.updateUserBalance(1L, new BigDecimal(100));
    }
}

6. NEVER

@Service
public class StatsService {
    
    @Transactional(propagation = Propagation.REQUIRED)
    public void collectStats() {
        // Некая обработка
        processData();
        
        // Это выбросит исключение, так как NEVER запрещает транзакции
        // recordStatistics(); // ОШИБКА!
    }
    
    @Transactional(propagation = Propagation.NEVER)
    public void recordStatistics() {
        // НИКОГДА не должна выполняться в транзакции
        // Если вызвать в контексте транзакции - выбросит исключение
        Stat stat = new Stat();
        stat.setTimestamp(System.currentTimeMillis());
        statRepository.save(stat);
    }
}

7. NESTED

@Service
public class OrderProcessingService {
    
    @Transactional(propagation = Propagation.REQUIRED)
    public void processCompleteOrder(Order order) {
        try {
            // Основная обработка
            saveOrder(order);
            
            // Платёж в вложенной транзакции (savepoint)
            processPayment(order);
            
            updateInventory(order);
        } catch (PaymentException e) {
            // Если платёж fails - откатываем ТОЛЬКО платёж
            // Остальное остаётся
            System.out.println("Платёж не прошёл");
        }
    }
    
    @Transactional(propagation = Propagation.NESTED)
    public void processPayment(Order order) {
        // Вложенная транзакция (savepoint)
        // Может откатиться независимо от родителя
        if (!validatePayment(order)) {
            throw new PaymentException("Invalid payment");
        }
        paymentRepository.save(order.getPayment());
    }
}

Таблица всех уровней Propagation

УровеньСуществует?Создаёт?Исключение?Использование
REQUIREDДа → используйНет → создай-По умолчанию
REQUIRES_NEWДа → приостановиВсегда → создай-Логирование, аудит
SUPPORTSДа → используйНет → нет-Read-only операции
NOT_SUPPORTEDДа → приостановиНет → нет-Операции вне транзакции
MANDATORYДа → используйНет → ошибкаДаОбязательная транзакция
NEVERДа → ошибкаНет → нетДаЗапрет транзакции
NESTEDДа → savepointНет → savepoint-Частичный откат

Практический пример: выбор правильного уровня

@Service
public class CompleteOrderService {
    
    @Transactional(propagation = Propagation.REQUIRED)
    public void completeOrder(Long orderId) {
        Order order = getOrder(orderId);
        order.setStatus("COMPLETED");
        orderRepository.save(order);
        
        // ТРЕБУЕТ_НОВ - логирование независимо
        logOrderCompletion(orderId);
        
        // SUPPORTS - может быть с транзакцией или без
        sendConfirmationEmail(orderId);
        
        // NESTED - может откатиться отдельно
        awardLoyaltyPoints(orderId);
    }
    
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void logOrderCompletion(Long orderId) {
        // Всегда в отдельной транзакции
    }
    
    @Transactional(propagation = Propagation.SUPPORTS)
    public void sendConfirmationEmail(Long orderId) {
        // Может быть с транзакцией
    }
    
    @Transactional(propagation = Propagation.NESTED)
    public void awardLoyaltyPoints(Long orderId) {
        // Вложенная транзакция - может откатиться частично
    }
}

Когда использовать какой уровень

  • REQUIRED: по умолчанию для большинства операций
  • REQUIRES_NEW: логирование, аудит, операции, которые должны сохраниться
  • SUPPORTS: read-only операции, которые не требуют транзакции
  • NOT_SUPPORTED: операции, которые должны быть вне транзакции
  • MANDATORY: критичные операции, которые требуют контекста транзакции
  • NEVER: операции, которые НИКОГДА не должны быть в транзакции
  • NESTED: сложная обработка с возможностью частичного отката

Правильный выбор уровня propagation критичен для обеспечения консистентности данных и правильного управления транзакциями в распределённых системах.

Что такое уровни propagation в транзакциях? | PrepBro