HibernateMiddleCoding
Как настроить кэш второго уровня в Hibernate? Назовите несколько провайдеров кэша.
Кэш второго уровня настраивается через hibernate.cache.use_second_level_cache=true и указание фабрики региона (JCacheRegionFactory для Ehcache, RedissonRegionFactory для Redis). Сущность маркируется @Cache с выбранной стратегией конкурентности.
Шаг 1: Подключение провайдера
Hibernate не включает провайдер кэша — нужно выбрать и подключить зависимость. Наиболее популярные варианты:
Ehcache 3 (через JCache/JSR-107):
<dependency>
<groupId>org.hibernate.orm</groupId>
<artifactId>hibernate-jcache</artifactId>
<version>6.4.4.Final</version>
</dependency>
<dependency>
<groupId>org.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>3.10.8</version>
</dependency>
Caffeine (in-memory, без TTL-конфигурации через XML):
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
<version>3.1.8</version>
</dependency>
Redisson (Redis-based, для кластера):
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-hibernate-6</artifactId>
<version>3.27.2</version>
</dependency>
Шаг 2: Конфигурация Hibernate (Spring Boot)
spring.jpa.properties.hibernate.cache.use_second_level_cache=true
spring.jpa.properties.hibernate.cache.use_query_cache=true
spring.jpa.properties.hibernate.cache.region.factory_class=org.hibernate.cache.jcache.JCacheRegionFactory
spring.jpa.properties.hibernate.javax.cache.provider=org.ehcache.jsr107.EhcacheCachingProvider
spring.jpa.properties.hibernate.javax.cache.uri=classpath:ehcache.xml
spring.jpa.properties.hibernate.generate_statistics=true
Шаг 3: Конфигурация Ehcache (ehcache.xml)
<?xml version="1.0" encoding="UTF-8"?>
<config xmlns="http://www.ehcache.org/v3">
<!-- Дефолтный шаблон для всех регионов -->
<default-template id="default">
<expiry>
<ttl unit="minutes">10</ttl>
</expiry>
<heap unit="entries">1000</heap>
</default-template>
<!-- Регион для Product -->
<cache alias="com.example.Product" uses-template="default">
<expiry>
<ttl unit="hours">1</ttl>
</expiry>
<heap unit="entries">5000</heap>
<off-heap unit="MB">100</off-heap>
</cache>
<!-- Регион для кэша запросов -->
<cache alias="default-query-results-region" uses-template="default"/>
<cache alias="default-update-timestamps-region">
<expiry><none/></expiry>
<heap unit="entries">5000</heap>
</cache>
</config>
Шаг 4: Маркировка сущностей
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
@Entity
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE, region = "com.example.Product")
public class Product {
@Id @GeneratedValue
private Long id;
private String name;
private BigDecimal price;
// Коллекции кэшируются отдельно!
@OneToMany(mappedBy = "product")
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
private List<Review> reviews;
}
Провайдеры: сравнение
- Ehcache 3 — зрелый, поддерживает off-heap и disk tiers, JCache-совместимый, хорошо документирован. Выбор для монолитов.
- Caffeine — максимально быстрый in-memory кэш, минимальная конфигурация, но без TTL через XML и без persistence. Выбор для микросервисов без внешнего кэша.
- Redisson (Redis) — распределённый кэш для кластеров. Конфигурируется через
redisson.yaml, поддерживает репликацию. Добавляет сетевую задержку. - Infinispan — поддерживает TRANSACTIONAL стратегию, подходит для JBoss/WildFly стека.
Мониторинг через Statistics
Statistics stats = sessionFactory.unwrap(SessionFactory.class).getStatistics();
long hits = stats.getSecondLevelCacheHitCount();
long misses = stats.getSecondLevelCacheMissCount();
double hitRate = (double) hits / (hits + misses) * 100;
log.info("L2 cache hit rate: {}%", hitRate);
Подводные камни
- Регион
default-update-timestamps-regionдля кэша запросов должен иметьexpiry = none— иначе инвалидация кэша запросов сломается и вы будете получать устаревшие результаты. - Коллекции (
@OneToMany,@ManyToMany) не кэшируются автоматически при наличии@Cacheна сущности — каждая коллекция требует отдельной аннотации@Cache. - В кластере с несколькими JVM in-memory провайдеры (Ehcache без терракотты, Caffeine) будут иметь рассинхронизированные кэши — необходим Redis или Infinispan с репликацией.
- Bulk JPQL UPDATE/DELETE не инвалидирует L2-кэш — после таких операций нужно вызвать
em.getEntityManagerFactory().getCache().evictAll(). - Размер heap в Ehcache задаётся в entries, а не в байтах по умолчанию — без off-heap tier большие объекты увеличивают давление на GC.
- Включение
hibernate.generate_statistics=trueв production добавляет заметный overhead — используйте Micrometer-метрики черезHibernateMetricsAutoConfigurationвместо Statistics API напрямую. - При использовании
READ_WRITEстратегии и JTA-транзакций требуется XA-совместимый провайдер кэша, иначе возникают проблемы двухфазного коммита.
Common mistakes
- Путать термин «second level cache config» с соседним механизмом Hibernate.
- Не называть границу lifecycle, transaction, thread или request для «second level cache config».
- Игнорировать production-эффекты «second level cache config»: latency, SQL shape, memory, security или observability.
What the interviewer is testing
- Попросить объяснить механизм «second level cache config» на минимальном примере.
- Проверить, видит ли кандидат failure mode и диагностику для «second level cache config».
- Уточнить, какие настройки или API меняют «second level cache config» в реальном сервисе.