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» в реальном сервисе.

Sources

Related topics