Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Что такое REQUIRED в propagation
Propagation.REQUIRED — это значение по умолчанию для управления транзакциями в Spring Framework (аннотация @Transactional). Оно определяет поведение методов внутри цепочки вызовов: если уже существует активная транзакция, использовать её; если нет, создать новую. Это самый частый выбор для большинства приложений.
Что такое Propagation
Propagation (распространение) — это механизм управления транзакциями в Spring, который определяет:
- Использовать ли существующую транзакцию
- Создавать ли новую
- Что делать, если транзакция уже активна
REQUIRED: поведение
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Service
public class OrderService {
@Autowired
private PaymentService paymentService;
@Autowired
private OrderRepository orderRepository;
// Propagation.REQUIRED — значение по умолчанию
@Transactional(propagation = Propagation.REQUIRED)
public Order createOrder(OrderRequest request) {
// Если нет транзакции — создаём новую
// Если уже есть — используем её
Order order = new Order(request);
order = orderRepository.save(order); // Внутри транзакции
// Вызываем paymentService
// Его @Transactional(REQUIRED) будет использовать
// ТУ ЖЕ транзакцию, что и createOrder
paymentService.processPayment(order);
return order;
}
}
@Service
public class PaymentService {
@Autowired
private PaymentRepository paymentRepository;
@Transactional(propagation = Propagation.REQUIRED)
public void processPayment(Order order) {
// Используем СУЩЕСТВУЮЩУЮ транзакцию из createOrder
// (не создаём новую)
Payment payment = new Payment(order);
paymentRepository.save(payment);
}
}
Жизненный цикл транзакции с REQUIRED
Сценарий 1: Нет активной транзакции
orderService.createOrder()
├─ BEGIN TRANSACTION ← Создаём новую
├─ INSERT order
└─ paymentService.processPayment()
├─ (использует существующую транзакцию)
├─ INSERT payment
└─ (транзакция продолжает работать)
COMMIT или ROLLBACK
Сценарий 2: Уже есть активная транзакция
@Transactional
public void doSomething() {
orderService.createOrder()
├─ (REQUIRED: используем существующую транзакцию)
├─ INSERT order
└─ paymentService.processPayment()
├─ (REQUIRED: используем ту же транзакцию)
├─ INSERT payment
└─ (всё в одной транзакции)
COMMIT или ROLLBACK (для всего блока)
}
Практический пример: REQUIRED vs другие значения
@Service
public class TransactionPropagationDemo {
@Autowired
private UserRepository userRepository;
// 1. REQUIRED (по умолчанию) — использовать или создать
@Transactional(propagation = Propagation.REQUIRED)
public void required() {
userRepository.save(new User("Alice"));
// Если вызова из @Transactional метода — используем его транзакцию
// Если вызова самостоятельно — создаём новую
}
// 2. REQUIRES_NEW — всегда создать новую
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void requiresNew() {
userRepository.save(new User("Bob"));
// ВСЕГДА создаём новую транзакцию, даже если уже есть
}
// 3. SUPPORTS — поддержать существующую, но не обязательна
@Transactional(propagation = Propagation.SUPPORTS)
public void supports() {
userRepository.save(new User("Charlie"));
// Если есть транзакция — используем
// Если нет — работаем без транзакции (Auto-commit)
}
// 4. NOT_SUPPORTED — НЕ использовать транзакцию
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void notSupported() {
// Если есть транзакция — приостанавливаем её
userRepository.save(new User("Diana"));
// После выполнения — возобновляем внешнюю транзакцию
}
// 5. MANDATORY — транзакция ОБЯЗАТЕЛЬНА
@Transactional(propagation = Propagation.MANDATORY)
public void mandatory() {
userRepository.save(new User("Eve"));
// Если нет активной транзакции → исключение!
}
// 6. NEVER — транзакция НЕ должна быть
@Transactional(propagation = Propagation.NEVER)
public void never() {
userRepository.save(new User("Frank"));
// Если есть активная транзакция → исключение!
}
}
Сценарий: Как REQUIRED объединяет транзакции
@Service
public class OrderProcessingService {
@Autowired
private OrderService orderService;
@Autowired
private NotificationService notificationService;
@Autowired
private LoggingService loggingService;
// Главная транзакция
@Transactional
public void processCompleteOrder(OrderRequest request) {
// ─────────── ОДНА ТРАНЗАКЦИЯ ───────────
// BEGIN TRANSACTION
// 1. orderService.create() — REQUIRED
// → использует СУЩЕСТВУЮЩУЮ транзакцию
Order order = orderService.create(request);
// 2. notificationService.notify() — REQUIRED
// → использует ТУ ЖЕ транзакцию
notificationService.notify(order);
// 3. loggingService.log() — REQUIRED
// → использует ТУ ЖЕ транзакцию
loggingService.log("Order created: " + order.getId());
// COMMIT (если всё ОК) или ROLLBACK (если ошибка)
}
// Если ошибка в любом методе → весь заказ откатывается
}
@Service
public class OrderService {
@Transactional(propagation = Propagation.REQUIRED)
public Order create(OrderRequest request) {
// Используем транзакцию от processCompleteOrder()
Order order = new Order(request);
// INSERT
return order;
}
}
@Service
public class NotificationService {
@Transactional(propagation = Propagation.REQUIRED)
public void notify(Order order) {
// Используем транзакцию от processCompleteOrder()
// INSERT в audit_log
}
}
REQUIRED vs REQUIRES_NEW: различие
@Service
public class ComparisonDemo {
@Transactional
public void mainTransaction() {
userRepository.save(new User("User1"));
// REQUIRED: все в одной транзакции
methodWithRequired(); // Ошибка → откатывает всё
// REQUIRES_NEW: в отдельной транзакции
methodWithRequiresNew(); // Ошибка → откатывает только эту
}
@Transactional(propagation = Propagation.REQUIRED)
public void methodWithRequired() {
userRepository.save(new User("User2"));
throw new RuntimeException("Error!");
// Результат: User1 и User2 НЕ сохраняются (откат всей транзакции)
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void methodWithRequiresNew() {
userRepository.save(new User("User3"));
throw new RuntimeException("Error!");
// Результат: User1 сохраняется, User3 НЕ сохраняется
}
}
Таблица всех значений Propagation
| Значение | Поведение | Ошибка если нет транзакции |
|---|---|---|
| REQUIRED | Используй существующую или создай новую | Нет, создаст новую |
| REQUIRES_NEW | Всегда создай новую | Нет, создаст новую |
| SUPPORTS | Используй если есть, иначе — без | Нет, работает без |
| NOT_SUPPORTED | Не используй транзакцию | Нет, отключает |
| MANDATORY | Должна быть обязательно | ДА, исключение |
| NEVER | Не должно быть транзакции | ДА, исключение |
| NESTED | Вложенная транзакция (savepoint) | Нет, создаст новую |
Когда использовать REQUIRED
// ✅ ИСПОЛЬЗУЙ REQUIRED для:
// 1. Обычных операций с БД
@Transactional(propagation = Propagation.REQUIRED)
public void saveUser(User user) {
userRepository.save(user);
}
// 2. Когда нужна атомарность группы операций
@Transactional(propagation = Propagation.REQUIRED)
public void transferMoney(Account from, Account to, BigDecimal amount) {
from.decreaseBalance(amount);
to.increaseBalance(amount);
accountRepository.save(from);
accountRepository.save(to);
// Либо оба переводы, либо ни один (атомарность)
}
// ❌ НЕ ИСПОЛЬЗУЙ REQUIRED если нужна отдельная транзакция
// Для этого используй REQUIRES_NEW
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void auditLog(String action) {
// Логирование должно быть независимо от основной операции
}
Заключение
REQUIRED — это режим распространения транзакций по умолчанию в Spring. Он автоматически использует существующую транзакцию или создаёт новую, что делает его идеальным выбором для 90% случаев. Это обеспечивает атомарность и консистентность для группы операций с БД. Выбирайте другие значения propagation только когда нужно специфичное поведение (например, REQUIRES_NEW для независимого логирования).