← Назад к вопросам
Какие знаешь способы использования транзакции в Spring?
2.0 Middle🔥 251 комментариев
#Spring Boot и Spring Data#Spring Framework
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Способы использования транзакций в Spring
Spring предоставляет несколько способов управления транзакциями, от декларативных аннотаций до программного управления. Рассмотрим все подходы.
1. @Transactional аннотация (Declarative Transaction Management)
Это самый удобный и рекомендуемый способ:
@Service
public class OrderService {
@Autowired
private OrderRepository orderRepository;
@Autowired
private PaymentRepository paymentRepository;
// Простая транзакция
@Transactional
public Order createOrder(OrderRequest request) {
Order order = new Order(request);
orderRepository.save(order);
return order;
}
// Если методы выбросят исключение, транзакция откатится
@Transactional
public void transferMoney(Long fromId, Long toId, BigDecimal amount) {
Account from = accountRepository.findById(fromId)
.orElseThrow(() -> new AccountNotFoundException(fromId));
Account to = accountRepository.findById(toId)
.orElseThrow(() -> new AccountNotFoundException(toId));
from.setBalance(from.getBalance().subtract(amount));
to.setBalance(to.getBalance().add(amount));
accountRepository.save(from);
accountRepository.save(to);
// Если здесь выбросится исключение, оба save откатятся
}
}
Spring автоматически создаёт proxy, который обёртывает метод в транзакцию.
2. @Transactional с параметрами
Управляем поведением транзакции через параметры:
@Service
public class OrderService {
// Только чтение (оптимизация)
@Transactional(readOnly = true)
public Order getOrder(Long id) {
return orderRepository.findById(id).orElseThrow();
}
// Откат при определённых исключениях
@Transactional(rollbackFor = {PaymentException.class, InvalidOrderException.class})
public void processOrder(Long orderId) {
// ...
}
// Не откатывать при определённых исключениях
@Transactional(noRollbackFor = {WarningException.class})
public void processWithWarnings(Long orderId) {
// WarningException не вызовет откат
}
// Timeout транзакции (в секундах)
@Transactional(timeout = 30)
public void longRunningOperation() {
// Если операция дольше 30 сек, откатится
}
// Isolation level
@Transactional(isolation = Isolation.READ_COMMITTED)
public void updateAccount(Long id, BigDecimal amount) {
// ...
}
// Propagation
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void createAuditLog(String message) {
// Новая независимая транзакция
// Даже если родительская откатится, лог запишется
}
}
3. Propagation (Распространение транзакций)
Как вложенные методы участвуют в транзакции:
@Service
public class OrderService {
@Autowired
private PaymentService paymentService;
@Autowired
private LogService logService;
// REQUIRED (default): использует существующую или создаёт новую
@Transactional(propagation = Propagation.REQUIRED)
public void createOrder(Order order) {
// Если вызвана из другой @Transactional метода,
// использует её транзакцию
orderRepository.save(order);
}
// REQUIRES_NEW: создаёт новую транзакцию, даже если уже есть
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void saveAuditLog(String message) {
// Новая независимая транзакция
// Если родительская откатится, лог останется
auditRepository.save(new AuditLog(message));
}
// NESTED: подтранзакция (savepoint в БД)
@Transactional(propagation = Propagation.NESTED)
public void processPaymentWithFallback(Payment payment) {
try {
paymentService.process(payment);
} catch (PaymentException e) {
// Откатывает только до savepoint
// Основная транзакция продолжается
}
}
// MANDATORY: требует существующей транзакции
@Transactional(propagation = Propagation.MANDATORY)
public void updateInventory(String itemId) {
// Выбросит ошибку, если вызвана не из @Transactional метода
}
// SUPPORTS: если транзакция есть, использует её
@Transactional(propagation = Propagation.SUPPORTS)
public void optionalTransaction() {
// Может быть вызвана с транзакцией или без
}
// NOT_SUPPORTED: выполняется БЕЗ транзакции
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void nonTransactional() {
// Всегда выполняется без транзакции
}
}
4. Isolation Levels (Уровни изоляции)
@Service
public class AccountService {
// DEFAULT: использует БД default (обычно READ_COMMITTED)
@Transactional(isolation = Isolation.DEFAULT)
public void updateBalance(Long id, BigDecimal amount) {}
// READ_UNCOMMITTED: видит неподтвержденные данные других транзакций
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
public void quickRead() {
// Быстро, но может читать грязные данные
}
// READ_COMMITTED: только подтвержденные данные (default PostgreSQL)
@Transactional(isolation = Isolation.READ_COMMITTED)
public void normalRead() {
// Баланс между скоростью и консистентностью
}
// REPEATABLE_READ: повторяемое чтение (может быть phantom read)
@Transactional(isolation = Isolation.REPEATABLE_READ)
public void stableRead() {
// Одно значение всегда читается одинаково
}
// SERIALIZABLE: максимальная изоляция
@Transactional(isolation = Isolation.SERIALIZABLE)
public void criticalOperation() {
// Как будто транзакции выполняются по очереди
// Медленно, но максимально безопасно
}
}
5. PlatformTransactionManager (Программное управление)
Для более точного контроля используем PlatformTransactionManager:
@Service
public class OrderService {
@Autowired
private PlatformTransactionManager transactionManager;
@Autowired
private OrderRepository orderRepository;
public Order createOrderManually(Order order) {
// Создаём определение транзакции
TransactionDefinition def = new DefaultTransactionDefinition();
// Начинаем транзакцию
TransactionStatus status = transactionManager.getTransaction(def);
try {
orderRepository.save(order);
// Фиксируем изменения
transactionManager.commit(status);
return order;
} catch (Exception e) {
// Откатываем
transactionManager.rollback(status);
throw e;
}
}
// С параметрами
public void processWithTimeout(Order order) {
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
def.setTimeout(30); // 30 секунд
def.setReadOnly(false);
TransactionStatus status = transactionManager.getTransaction(def);
try {
// Бизнес логика
orderRepository.save(order);
transactionManager.commit(status);
} catch (Exception e) {
transactionManager.rollback(status);
throw e;
}
}
}
6. TransactionTemplate (удобная обёртка)
Проще, чем PlatformTransactionManager:
@Service
public class OrderService {
@Autowired
private TransactionTemplate transactionTemplate;
@Autowired
private OrderRepository orderRepository;
// С возвращаемым значением
public Order createOrder(Order order) {
return transactionTemplate.execute(status -> {
orderRepository.save(order);
return order;
// Если выбросится исключение, откатится
// Если вернёт значение, коммитится
});
}
// Без возвращаемого значения
public void processOrders(List<Order> orders) {
transactionTemplate.executeWithoutResult(status -> {
for (Order order : orders) {
orderRepository.save(order);
}
});
}
// С кастомной конфигурацией
public void processWithCustomIsolation(Order order) {
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setIsolationLevel(TransactionDefinition.ISOLATION_SERIALIZABLE);
TransactionTemplate template = new TransactionTemplate(
transactionManager, def);
template.execute(status -> {
orderRepository.save(order);
return null;
});
}
}
7. Reactive Транзакции
Для реактивных приложений (Spring WebFlux + R2DBC):
@Service
public class ReactiveOrderService {
@Autowired
private ReactiveTransactionManager transactionManager;
@Autowired
private OrderRepository orderRepository; // R2DBC
@Transactional
public Mono<Order> createOrder(Order order) {
return orderRepository.save(order)
.doOnError(e -> System.err.println("Error saving order"));
}
public Mono<Order> createOrderManually(Order order) {
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
return Mono.using(
() -> transactionManager.getTransaction(def),
status -> orderRepository.save(order)
.doOnNext(__ -> transactionManager.commit(status))
.doOnError(e -> transactionManager.rollback(status)),
TransactionSynchronizationManager::clear
);
}
}
8. Транзакции в тестах
@SpringBootTest
@Transactional // Автоматический откат после каждого теста
public class OrderServiceTest {
@Autowired
private OrderService orderService;
@Autowired
private OrderRepository orderRepository;
@Test
public void testCreateOrder() {
Order order = new Order();
orderService.createOrder(order);
// Сохранённый заказ видим в тесте
Order saved = orderRepository.findById(order.getId()).orElseThrow();
assertNotNull(saved);
// После теста всё откатится
}
@Test
@Rollback(false) // Не откатывать
public void testPersistOrder() {
Order order = new Order();
orderService.createOrder(order);
// Данные останутся в БД после теста
}
}
9. Event Publication на основе транзакций
Публикация событий после успешного коммита:
@Service
public class OrderService {
@Autowired
private ApplicationEventPublisher eventPublisher;
@Autowired
private OrderRepository orderRepository;
@Transactional
public void createOrder(Order order) {
orderRepository.save(order);
// Событие публикуется ПОСЛЕ успешного коммита
eventPublisher.publishEvent(new OrderCreatedEvent(order));
}
}
// Слушатель
@Component
public class OrderCreatedListener {
@EventListener
@Transactional
public void onOrderCreated(OrderCreatedEvent event) {
// Может быть в отдельной транзакции
// Даже если это упадёт, заказ уже создан
}
}
10. Best Practices
@Service
public class OrderService {
// ✅ Хорошо: транзакция на методе сервиса
@Transactional
public void createOrder(Order order) {
// Логика создания
}
// ✅ Хорошо: readOnly для оптимизации
@Transactional(readOnly = true)
public Order getOrder(Long id) {
return orderRepository.findById(id).orElseThrow();
}
// ✅ Хорошо: явное управление откатом
@Transactional(rollbackFor = OrderException.class)
public void processOrder(Order order) {
// ...
}
// ❌ Плохо: транзакция на controller'е
// @Transactional
// public ResponseEntity<Order> createOrder(...) { }
// ❌ Плохо: слишком длинная транзакция
// @Transactional
// public void slowOperation() { Thread.sleep(1000); }
// ❌ Плохо: @Transactional на интерфейсе (иногда не работает)
// Используй на реализации
}
Сравнение подходов
| Подход | Удобство | Контроль | Когда использовать |
|---|---|---|---|
| @Transactional | Очень высокое | Средний | Большинство случаев |
| PlatformTransactionManager | Среднее | Полный | Сложная логика |
| TransactionTemplate | Высокое | Высокий | Гибридный подход |
| Reactive | Среднее | Полный | WebFlux приложения |
| Тесты | Очень высокое | Средний | Изоляция данных в тестах |