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

Как изменять поведение аннотации Transactional в Spring

1.0 Junior🔥 121 комментариев
#Spring Framework

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

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

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

Ответ

Как изменять поведение аннотации @Transactional в Spring

@Transactional — один из самых мощных механизмов Spring для управления транзакциями. Рассмотрю все параметры и способы кастомизации.

1. Основные параметры @Transactional

@Transactional(
    value = "transactionManager",           // Какой TransactionManager использовать
    propagation = Propagation.REQUIRED,     // Как распространяется транзакция
    isolation = Isolation.READ_COMMITTED,   // Уровень изоляции
    timeout = 30,                           // Таймаут в секундах
    readOnly = false,                       // Только чтение
    rollbackFor = Exception.class,          // На какие исключения откатывать
    noRollbackFor = IgnorableException.class // Какие исключения не откатывать
)
public void complexOperation() {
    // код
}

2. Propagation — как распространяется транзакция

@Service
public class OrderService {
    
    // REQUIRED (по умолчанию) — используй существующую, или создай новую
    @Transactional(propagation = Propagation.REQUIRED)
    public void saveOrder(Order order) {
        orderRepository.save(order);
        // Если уже в транзакции — используем её, иначе создаём новую
    }
    
    // REQUIRES_NEW — всегда создай новую, текущую приостанови
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void auditLog(String action) {
        auditRepository.log(action);
        // Даже если основная транзакция откатится, аудит сохранится
    }
    
    // NESTED — вложенная точка сохранения
    @Transactional(propagation = Propagation.NESTED)
    public void processPayment(Payment payment) {
        paymentService.charge(payment);
    }
    
    // MANDATORY — должна быть существующая транзакция
    @Transactional(propagation = Propagation.MANDATORY)
    public void criticalOperation() {
        // Выбросит IllegalTransactionStateException если нет транзакции
    }
}

3. Isolation — уровни изоляции

@Service
public class AccountService {
    
    // READ_COMMITTED — стандарт, грязные чтения запрещены
    @Transactional(isolation = Isolation.READ_COMMITTED)
    public void updateBalance(Long accountId, BigDecimal amount) {
        // Безопасно от грязных чтений
    }
    
    // REPEATABLE_READ — нет неповторяемых чтений
    @Transactional(isolation = Isolation.REPEATABLE_READ)
    public void processPayment(Long accountId) {
        BigDecimal balance1 = getBalance(accountId);
        BigDecimal balance2 = getBalance(accountId);
        // balance1 == balance2 гарантировано
    }
    
    // SERIALIZABLE — максимальная изоляция
    @Transactional(isolation = Isolation.SERIALIZABLE)
    public void criticalAudit() {
        // Полная изоляция, очень медленно
    }
}

4. ReadOnly — оптимизация для чтения

@Service
public class ReportService {
    
    // Для чтения — помечай readOnly = true
    @Transactional(readOnly = true)
    public List<OrderReport> getMonthlyReport(YearMonth month) {
        return orderRepository.findByMonth(month);
        // Оптимизировано для чтения
    }
    
    // Для записи — по умолчанию readOnly = false
    @Transactional
    public void saveOrder(Order order) {
        orderRepository.save(order);
    }
}

5. Timeout — таймаут транзакции

@Service
public class DataProcessingService {
    
    // Таймаут 30 секунд
    @Transactional(timeout = 30)
    public void processLargeDataset(Long datasetId) {
        // Если дольше 30 сек — откатится
    }
}

6. RollbackFor / NoRollbackFor

@Service
public class PaymentService {
    
    // Откатывай также на checked исключения
    @Transactional(rollbackFor = IOException.class)
    public void processWithFile(File file) throws IOException {
        // Откатится и на IOException
    }
    
    // НЕ откатывай на определённые исключения
    @Transactional(noRollbackFor = IgnorableException.class)
    public void resilientOperation() {
        try {
            riskyOperation();
        } catch (IgnorableException e) {
            logger.warn("Игнорируемая ошибка", e);
            // Транзакция НЕ откатится
        }
    }
}

7. Выбор TransactionManager

@Configuration
public class TransactionConfig {
    
    @Bean("primaryTxManager")
    public PlatformTransactionManager primaryTransactionManager(DataSource ds) {
        return new DataSourceTransactionManager(ds);
    }
    
    @Bean("secondaryTxManager")
    public PlatformTransactionManager secondaryTransactionManager(
            DataSource secondaryDs) {
        return new DataSourceTransactionManager(secondaryDs);
    }
}

@Service
public class MultiDatabaseService {
    
    // Используй первый transaction manager
    @Transactional("primaryTxManager")
    public void saveInPrimary(Entity entity) {
        primaryRepository.save(entity);
    }
    
    // Используй второй transaction manager
    @Transactional("secondaryTxManager")
    public void saveInSecondary(Entity entity) {
        secondaryRepository.save(entity);
    }
}

8. Таблица Propagation режимов

РежимПоведение
REQUIREDИспользуй существующую или создай новую
REQUIRES_NEWВсегда новая (текущую приостановит)
NESTEDВложенная транзакция (savepoint)
MANDATORYОбязательна существующая
SUPPORTSИспользуй если есть, если нет — без транзакции
NOT_SUPPORTEDВыполни без транзакции
NEVERНе должна быть транзакция

9. Практический пример

@Service
public class ComplexOrderService {
    
    @Transactional(
        propagation = Propagation.REQUIRED,
        isolation = Isolation.READ_COMMITTED,
        timeout = 60,
        readOnly = false
    )
    public Long createOrderWithItems(OrderDTO dto) {
        Order order = new Order(dto);
        orderRepository.save(order);
        
        for (ItemDTO itemDto : dto.getItems()) {
            Item item = new Item(itemDto);
            item.setOrder(order);
            itemRepository.save(item);
        }
        
        return order.getId();
    }
    
    @Transactional(readOnly = true)
    public OrderDTO getOrder(Long orderId) {
        Order order = orderRepository.findById(orderId)
            .orElseThrow(() -> new NotFoundException("Order not found"));
        return new OrderDTO(order);
    }
    
    @Transactional(
        propagation = Propagation.REQUIRES_NEW,
        rollbackFor = Exception.class
    )
    public void logTransaction(String action, Long orderId) {
        auditRepository.save(new AuditLog(action, orderId));
    }
}

Best Practices

  1. Помечай readOnly = true для запросов только на чтение — оптимизирует производительность
  2. Понимай propagation для вложенных операций — часто нужны REQUIRES_NEW для аудита
  3. Выбирай правильный isolation уровень — баланс между безопасностью и производительностью
  4. Указывай timeout для потенциально долгих операций
  5. Откатывай на checked исключения явно если нужно
  6. Тестируй откат — убедись, что откатываешь правильно

@Transactional — мощный инструмент для управления транзакциями. Правильная конфигурация критична для надёжности приложения.

Как изменять поведение аннотации Transactional в Spring | PrepBro