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

Какие знаешь способы использования транзакции в 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 приложения
ТестыОчень высокоеСреднийИзоляция данных в тестах