← Назад к вопросам
Что такое уровни 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 критичен для обеспечения консистентности данных и правильного управления транзакциями в распределённых системах.