middle
Как работает @Transactional в Spring?
@Transactional – аннотация для декларативного управления транзакциями. Spring оборачивает вызов метода в транзакцию через прокси: начинает перед выполнением, коммитит при успехе, откатывает при RuntimeException/Error. При checked exception по умолчанию – commit.
Механизм работы
- Spring создаёт прокси вокруг бина с
@Transactional - При вызове метода прокси открывает транзакцию через
PlatformTransactionManager - При успехе – commit
- При
RuntimeException/Error– rollback - При 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. Частая ошибка – полагать, что любое исключение откатит транзакцию.