Кэширование Hibernate
Hibernate использует несколько механизмов с разной областью жизни. Они не взаимозаменяемы.
Первый уровень
Это обязательный Persistence Context. Он хранит managed-сущности в пределах EntityManager/Session и гарантирует идентичность объекта. После clear или закрытия контекста записи исчезают.
Второй уровень
Необязательный общий кэш EntityManagerFactory. Он хранит состояние сущностей и коллекций между транзакциями. Включается отдельно, требует провайдера и явного выбора кэшируемых регионов.
При попадании в L2 Hibernate всё равно создаёт объект текущего контекста. Кэш не хранит «живой» managed-экземпляр между потоками.
Кэш запросов
Хранит не сущности, а результат запроса — обычно набор идентификаторов и scalar-значений. Для сущностей затем нужен L2 либо чтение таблицы. Его выгода зависит от стабильности параметров и частоты инвалидирования.
Стратегии конкуренции
READ_ONLY— данные не меняются через Hibernate;NONSTRICT_READ_WRITE— допускается короткое окно устаревших данных;READ_WRITE— координирует обновления, но не заменяет транзакции БД;TRANSACTIONAL— требует транзакционного провайдера и инфраструктуры.
Поддержка зависит от Провайдеры кэша второго уровня Hibernate.
Когда L2 вреден
- данные часто изменяются;
- несколько приложений меняют таблицу вне механизма инвалидирования;
- результат почти всегда уникален;
- кэш скрывает неоптимальный SQL;
- нет метрик hit/miss, размера и времени вытеснения.
Вопросы для повторения
- L1 и L2 одинаковы? Нет: L1 принадлежит контексту и обязателен, L2 общий и опционален.
- Кэш запросов хранит готовые сущности? Обычно нет, он хранит ключи или scalar-результаты.
- Заменяет ли L2 Redis? Нет, назначение и модель согласованности отличаются.
- Что произойдёт после bulk update? Контекст и кэш могут содержать устаревшие данные; нужна явная очистка/инвалидация.
- С чего начинать оптимизацию? С измерения SQL, плана и числа запросов, а не с включения кэша.
Связано: Hibernate, EntityManager, SQL запросы VS Hibernate.