Что происходит с сохранением данных при исключении в методах save в базе данных
Что проверяют
Знаете ли вы правила отката в Spring и понимаете ли, когда save вообще выполняет запрос. Ответ «транзакция откатится» слишком общий: интервьюер ждёт условий, при которых она не откатится, хотя исключение было.
Как отвечать
По пунктам, коротко и конкретно.
Откат по умолчанию происходит на непроверяемых исключениях, то есть на наследниках RuntimeException и Error. Проверяемое исключение транзакцию не откатывает, пока правило не задано явно:
@Transactional(rollbackFor = IOException.class)
public void store(Order order) throws IOException { ... }Дальше момент выполнения. В JPA вызов save для новой сущности не обязан немедленно отправить вставку: изменения копятся и уходят при сбросе контекста, то есть перед запросом или на коммите. Поэтому нарушение ограничения часто прилетает не на строке save, а позже, за пределами вашего блока перехвата.
Третье: границу транзакции задаёт прокси. Вызов аннотированного метода изнутри того же класса идёт мимо прокси, и аннотация не работает. А если дёргать репозиторий вовсе без внешней транзакции, каждый вызов станет отдельной транзакцией, и после ошибки на третьей сущности первые две останутся в базе.
Отдельно скажите про исключение из правил: запись в журнал аудита, которая должна пережить откат, делается в транзакции с распространением «требуется новая».
Где спотыкаются
Перехватывают исключение внутри транзакционного метода и спокойно продолжают. Транзакция уже помечена только на откат, на коммите прилетит отказ, а контекст сохранения после неудачного сброса переиспользовать нельзя.
Считают, что откат чинит объекты в памяти. База вернётся в прежнее состояние, а поля сущностей, номера версий и всё, что успели положить в кэш приложения, останутся изменёнными.
Ждут, что перехват внутри вложенного метода спасёт внешнюю транзакцию. Если исключение уже пересекло транзакционную границу, внешняя транзакция обречена, и обрабатывать такую ошибку надо снаружи, отдельной транзакцией.