Gymterview
middle

Как работает @Transactional в Spring?

@Transactional – аннотация для декларативного управления транзакциями. Spring оборачивает вызов метода в транзакцию через прокси: начинает перед выполнением, коммитит при успехе, откатывает при RuntimeException/Error. При checked exception по умолчанию – commit.

Механизм работы

  1. Spring создаёт прокси вокруг бина с @Transactional
  2. При вызове метода прокси открывает транзакцию через PlatformTransactionManager
  3. При успехе – commit
  4. При RuntimeException / Error – rollback
  5. При checked exception – commit (по умолчанию!)
Пример
@Service
public class TransferService {

    @Transactional
    public void transfer(Long fromId, Long toId, BigDecimal amount) {
        Account from = accountRepository.findById(fromId).orElseThrow();
        Account to = accountRepository.findById(toId).orElseThrow();
        from.setBalance(from.getBalance().subtract(amount));
        to.setBalance(to.getBalance().add(amount));
        accountRepository.save(from);
        accountRepository.save(to);
        // если здесь произойдёт RuntimeException -- все изменения откатятся
    }
}

Основные параметры

Пример
@Transactional(
    propagation = Propagation.REQUIRED,        // тип распространения
    isolation = Isolation.READ_COMMITTED,       // уровень изоляции
    timeout = 30,                               // таймаут в секундах
    readOnly = true,                            // только для чтения (оптимизация)
    rollbackFor = Exception.class,              // откат при checked exception
    noRollbackFor = BusinessException.class     // НЕ откатывать при этом исключении
)

readOnly = true

Подсказка для оптимизации: Hibernate отключает dirty checking, база может использовать read-реплику:

Пример
@Transactional(readOnly = true)
public List<User> findAllUsers() {
    return userRepository.findAll();
}

На собеседовании: критически важно знать, что checked exception НЕ откатывает транзакцию по умолчанию. Для банковских операций всегда указывайте rollbackFor = Exception.class. Частая ошибка – полагать, что любое исключение откатит транзакцию.