senior
Какие подводные камни есть у @Transactional?
Это один из самых популярных вопросов на собеседованиях. Подводные камни @Transactional – проверка глубины понимания Spring.
1. Self-invocation (вызов из того же класса)
@Transactional работает через прокси. Вызов метода внутри того же класса обходит прокси:
Пример
@Service
public class UserService {
@Transactional
public void updateUser(User user) {
userRepository.save(user);
}
public void processUser(Long userId) {
User user = userRepository.findById(userId).orElseThrow();
this.updateUser(user); // @Transactional НЕ сработает!
}
}
Решения: вынести в другой сервис или использовать self-injection (@Autowired private UserService self;).
2. Checked exceptions не вызывают откат
Пример
@Transactional // НЕ откатится при IOException!
public void saveFile(MultipartFile file) throws IOException {
userRepository.save(user);
fileStorage.store(file); // бросает IOException -- транзакция коммитится!
}
// Правильно:
@Transactional(rollbackFor = Exception.class)
public void saveFile(MultipartFile file) throws IOException { }
3. @Transactional на private методе не работает
Spring AOP не может создать прокси для private-метода.
4. Проглатывание исключения
Пример
@Transactional
public void process() {
try {
riskyOperation();
} catch (Exception e) {
log.error("Error", e);
// исключение проглочено -- транзакция закоммитится,
// хотя данные могут быть в некорректном состоянии
}
}
5. Долгие операции внутри транзакции
Пример
@Transactional
public void processOrder(Order order) {
orderRepository.save(order);
emailService.sendEmail(order); // долгая операция -- блокирует соединение с БД!
}
Отправку уведомлений нужно выносить за пределы транзакции или делать асинхронно.
6. Не указан TransactionManager при нескольких DataSource
Пример
@Transactional("secondaryTransactionManager")
public void saveToSecondaryDb(Data data) { }
На собеседовании: это вопрос для senior-уровня. Интервьюер ждёт минимум 3-4 подводных камня с примерами. Главные: self-invocation, checked exceptions, private-методы. Частая ошибка – не знать про self-invocation или думать, что любое исключение откатит транзакцию.