Приходилось ли работать с транзакциями в Java
Что проверяют
Понимаете ли вы, где проходят границы транзакции и почему @Transactional иногда не работает вовсе. Вопрос звучит как про опыт, но за ним всегда идут уточнения про прокси, откат и уровни изоляции.
Как отвечать
Сначала уровни управления, снизу вверх:
- JDBC:
setAutoCommit(false), затемcommit()илиrollback(), при необходимостиSavepointдля частичного отката. - Spring: декларативно через
@Transactionalповерх прокси. Важные параметры этоpropagation(REQUIREDпо умолчанию,REQUIRES_NEWдля отдельной транзакции,NESTEDчерез точку сохранения),isolation,readOnly,rollbackFor,timeout. - JPA: транзакция задаёт границы persistence context. Изменения сущностей уходят в базу при сбросе перед коммитом, а после закрытия контекста ленивые связи уже не подгрузить.
Назовите умолчания, на которых ловятся все: откат происходит на RuntimeException и Error, а на проверяемое исключение не происходит, пока не указан rollbackFor. Уровень изоляции в итоге определяет база: в PostgreSQL грязного чтения нет вообще, и READ UNCOMMITTED ведёт себя как READ COMMITTED.
Если спросят про несколько сервисов, честный ответ: одной аннотацией это не решается. Либо распределённая транзакция через JTA, либо, что сегодня чаще, сага и исходящая очередь с идемпотентными операциями.
Где спотыкаются
Вызывают транзакционный метод изнутри того же бина. Прокси в этом случае не участвует, транзакции нет, а тест зелёный лишь потому, что транзакция открыта где-то выше по стеку. То же самое с private методами.
Ловят исключение внутри транзакции и продолжают работу. Транзакция уже помечена как rollback-only, и на коммите прилетает UnexpectedRollbackException, причём в месте, где ничего не падало.
Держат внутри транзакции внешние вызовы: HTTP, отправку письма, публикацию в брокер. Соединение из пула занято всё это время, а откат базы уже отправленное письмо не отменит. Отдельно режет слух рассказ про «атомарность между разными сервисами и базами одной аннотацией»: так это не работает.