Что такое кэш второго уровня (L2) в Hibernate?

Ответ

Кэш второго уровня (L2 cache) в Hibernate — это кэш, общий для всех сессий (экземпляров Session), созданных одной фабрикой сессий (SessionFactory). Его цель — избежать повторных запросов к базе данных для одних и тех же данных в разных транзакциях или пользовательских сессиях.

Как работает:

  1. При загрузке сущности по идентификатору Hibernate сначала проверяет кэш первого уровня (сессии).
  2. Если там нет, проверяется кэш второго уровня.
  3. Если сущность найдена в L2, она возвращается (без запроса к БД).
  4. Если нет — выполняется SQL-запрос, и результат помещается в оба кэша.

Настройка и использование:

  1. Включение и провайдер (на примере Ehcache):
    <!-- hibernate.cfg.xml -->
    <property name="hibernate.cache.use_second_level_cache">true</property>
    <property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</property>
  2. Аннотирование сущности:
    @Entity
    @Cacheable
    @org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
    public class Product {
        // ...
    }
  3. Стратегии согласованности (CacheConcurrencyStrategy):
    • READ_ONLY: Для неизменяемых данных.
    • READ_WRITE: Использует мягкие блокировки для обновляемых данных.
    • NONSTRICT_READ_WRITE: Обновления могут привести к временной несогласованности.
    • TRANSACTIONAL: Полная поддержка транзакций (требует поддержки JTA).

Важно: L2 кэш не кэширует результаты запросов по умолчанию. Для этого нужен отдельный кэш запросов, включаемый свойством hibernate.cache.use_query_cache=true.

Ответ 18+ 🔞

Давай разберём эту штуку, про которую все так умно говорят, а на деле половина нихуя не понимает, как оно работает. Кэш второго уровня в Hibernate, блядь.

Представь себе, ты в библиотеке, сука. Первый кэш (L1) — это твоя собственная, ёбта, голова. Ты что-то прочитал — запомнил. Пока ты не сдох (сессия не закрылась), ты это помнишь. Но ты же не один такой умный в этой библиотеке, верно? Там ещё другие читатели шастают.

Так вот, кэш второго уровня (L2) — это, блядь, общая тетрадка на столе, доступная всем, кто пришёл от одной и той же библиотекарши (SessionFactory). Один чувак нашёл ответ на сложный вопрос, записал его в эту тетрадку. Второй приходит, смотрит — о, ни хуя себе, ответ уже есть! И ему не надо идти в хранилище (базу данных) и перерывать все стеллажи. Экономия времени — овердохуищная.

Как эта магия происходит, на пальцах:

  1. Ты просишь Hibernate: «Дай мне товар с ID=5».
  2. Он сначала лезет тебе в карман (L1 кэш сессии): «А не помнишь ли ты сам?». Не помнишь.
  3. Потом смотрит на общую тетрадку на столе (L2 кэш): «А не записано ли тут?». Если записано — всё, пизда, конец запросу. Отдаёт тебе данные и доволен.
  4. Если и там пусто — тогда уже идёт в хранилище, долго и нудно ищет, приносит. И, как воспитанный, не только тебе в карман кладёт (L1), но и в общую тетрадку (L2) записывает, чтобы следующий мудак не дергал систему.

Чтобы это всё завелось, надо потанцевать с бубном:

  1. Сказать Хибернейту, что мы не нищие и хотим кэш. И указать, какую именно библиотеку (провайдера) для этой тетрадки будем использовать. Допустим, Ehcache.

    <!-- hibernate.cfg.xml -->
    <property name="hibernate.cache.use_second_level_cache">true</property>
    <property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</property>

    Без этих строчек L2 кэш — как хуй в пальто: вроде есть, но него не видно и пользы ноль.

  2. Надо пометить, какие именно наши сущности достойны быть в этой почётной тетрадке. А то туда всякую хуйню понапишут.

    @Entity
    @Cacheable // Говорим JPA, что эту хрень можно кэшировать
    @org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_WRITE) // А тут Хибернейту объясняем КАК
    public class Product {
        // ...
    }
  3. Стратегии (CacheConcurrencyStrategy) — это, блядь, правила пользования тетрадкой. Чтобы не было драк.

    • READ_ONLY: Для данных, которые никогда не меняются. Типа справочника стран. Пишем раз и навсегда. Быстро и надёжно.
    • READ_WRITE: Для данных, которые обновляются. Использует мягкие блокировки, чтобы, когда один пишет, другой не прочитал хуйню. Самый частый выбор.
    • NONSTRICT_READ_WRITE: «А, да похуй!». Обновления могут привести к тому, что кто-то на секунду прочитает старые данные. Зато быстрее. Рисковый вариант.
    • TRANSACTIONAL: Полная паранойя для суровых транзакционных систем. Требует JTA, чтоб всё было по-взрослому.

И главное, внимание, ёпта! Этот L2 кэш по умолчанию НЕ КЭШИРУЕТ результаты твоих хитрых HQL- или Criteria-запросов! Он кэширует только загрузку по ID.

То есть, если ты ищешь from Product where price > 1000, он каждый раз будет лезть в базу, даже если товары-то уже все в кэше лежат! Для этого нужен отдельный, блядь, кэш запросов. Включается вот так: hibernate.cache.use_query_cache=true

И каждый такой запрос надо в коде явно пометить как кэшируемый: .setCacheable(true).

Вот так вот, сука. Кажется, просто, а настроить и добиться работы — волнение ебать, терпения ноль ебать. Но когда заработает — красота.