Что происходит с кэшем первого уровня после коммита транзакции
Что проверяют
Понимаете ли, что кэш первого уровня привязан к сессии, а не к транзакции, и знаете ли, что с ним делает типичная настройка Spring. Заодно смотрят, не путаете ли вы его с процессорным кэшем.
Как отвечать
Кэш первого уровня это контекст персистентности: он живёт внутри Session или EntityManager и хранит по одному экземпляру сущности на идентификатор. Сам коммит его не очищает. Перед коммитом идёт flush, где сравнение со снимком находит изменения и отправляет запросы в базу, но объекты остаются в контексте управляемыми.
Практический ответ зависит от того, чем ограничена сессия:
- в Spring с
@Transactionalи контекстом на транзакцию после коммитаEntityManagerзакрывается, контекст умирает, а сущности становятся отсоединёнными. То есть после коммита кэша фактически нет; - при расширенном контексте или открытой сессии на весь запрос объекты остаются управляемыми, и повторный поиск по тому же идентификатору в базу не пойдёт.
Управляют этим руками: clear() сбрасывает весь контекст, detach убирает одну сущность, flush синхронизирует без коммита. В пакетной обработке flush и clear через каждые несколько сотен объектов обязательны, иначе контекст растёт и память заканчивается.
Где спотыкаются
Ждут свежих данных оттуда, где их нет. Сущность уже в контексте, соседняя транзакция обновила строку, а поиск по идентификатору вернёт прежний объект. Даже запрос на языке запросов, вытащив строку, отдаст уже имеющийся управляемый экземпляр, а не перезапишет его данными из базы.
Второе: трогают ленивое поле после завершения транзакции и получают LazyInitializationException. Контекст закрылся, догружать связь нечем. Данные нужно получить внутри транзакции или сразу вытянуть связь через join fetch.
Третье: путают уровни. Кэш первого уровня не отключается и никому, кроме своей сессии, не виден. Общий на приложение это второй уровень, он подключается осознанно и вместе с политикой обновления, иначе даёт устаревшие данные уже по всему сервису.