Короткий ответ
@Transactional — аннотация декларативного управления транзакциями: все операции с базой данных внутри помеченного метода (или класса) выполняются в рамках одной транзакции и либо целиком фиксируются (commit), либо при ошибке откатываются (rollback). Аннотация применяется на уровне метода или класса.
Как это работает подробнее
Spring оборачивает бин в AOP-прокси. Когда помеченный метод вызывается через этот прокси, Spring перед вызовом открывает транзакцию — берёт соединение из пула — а после успешного завершения метода выполняет commit. Если метод бросает исключение, транзакция откатывается, и база данных остаётся в согласованном состоянии.
- Откат по умолчанию: происходит только при непроверяемых исключениях —
RuntimeExceptionиError. - Checked-исключения: если метод бросает проверяемое исключение, нужно явно указать
rollbackFor, например@Transactional(rollbackFor = Exception.class). - Self-invocation: когда один метод бина вызывает другой
@Transactional-метод того же бина черезthis, вызов идёт в обход прокси, и новая транзакция не открывается. - Дополнительные настройки: через аннотацию можно задавать распространение транзакции (propagation), уровень изоляции (isolation) и другие параметры.
Пример кода
OrderService.java
@Service
public class OrderService {
private final OrderRepository orderRepository;
public OrderService(OrderRepository orderRepository) {
this.orderRepository = orderRepository;
}
@Transactional
public void placeOrder(Order order) {
orderRepository.save(order);
// если здесь возникнет RuntimeException, транзакция откатится
}
}Что использовать на практике
- Ставьте
@Transactionalна уровне сервиса, а не контроллера: так граница транзакции покрывает всю бизнес-операцию. - Если откат нужен при checked-исключении, обязательно задавайте
rollbackFor. - Помните, что методы репозиториев Spring Data JPA уже транзакционные по умолчанию.
Подводные камни
- Вызов должен идти через прокси: self-invocation через
thisне открывает транзакцию — выносите такой вызов в другой бин. - По умолчанию checked-исключения не откатывают транзакцию — это частый источник «молчаливых» ошибок.
- Не вешайте
@Transactionalна приватные методы: через прокси они не перехватываются.
Как отвечать на собеседовании
- Начните с определения: @Transactional объявляет границу транзакции на методе или классе → объясните механизм: AOP-прокси открывает транзакцию и делает commit или rollback → уточните условие отката по умолчанию (RuntimeException, Error) и назначение rollbackFor для checked-исключений → добавьте подводный камень self-invocation через this.