Ответ
Кэш второго уровня (L2 cache) в Hibernate — это кэш сессий (SessionFactory), общий для всех сессий (Session), созданных из одной фабрики. Он хранит загруженные сущности и коллекции, что позволяет избежать повторных запросов к БД в разных транзакциях.
Отличие от кэша первого уровня (L1):
- L1 cache привязан к конкретной сессии (
Session) и живет только во время её существования. - L2 cache разделяется между всеми сессиями приложения.
Конфигурация и использование:
- Выбор провайдера кэша: Ehcache, Infinispan, Hazelcast, Caffeine.
- Включение в
persistence.xmlили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> - Аннотирование сущностей:
@Entity @Cacheable // Включает сущность в L2 cache @org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_WRITE) // Стратегия public class Product { ... }
Стратегии согласованности (CacheConcurrencyStrategy):
- READ_ONLY: для неизменяемых данных.
- READ_WRITE: использует мягкие блокировки, обеспечивает согласованность для часто читаемых, но редко изменяемых данных.
- NONSTRICT_READ_WRITE: обновления кэша после коммита транзакции, возможны краткие периоды несогласованности.
- TRANSACTIONAL: полная поддержка транзакций (JTA), самая строгая.
Важные моменты:
- Кэшируются только идентифицируемые сущности (с
@Id). Запросы (Query) кэшируются отдельно (query cache). - При изменении данных через нативные SQL или другие приложения кэш может устареть — требуется инвалидация или использование стратегий с учетом этого.
Ответ 18+ 🔞
Да ты посмотри, какая тема подъехала — про кэш второго уровня в Hibernate! Ну это ж, блядь, целая эпопея, как у того Герасима с Муму, только тут не собаку топить, а запросы к базе оптимизировать. Слушай сюда, разжую.
Представь, L1 cache — это как твой личный холодильник в сессии. Открыл, взял пиво, закрыл. Никто, кроме тебя, туда не лазит. А L2 cache — это уже общий холодильник на всю хату, на все сессии от одной фабрики. Положил туда сущность — и любой другой чувак может её оттуда вытащить, не бегая каждый раз в магазин (то есть в базу данных). Удобно, ёпта!
Чем они вообще отличаются, эти кэши?
- L1 (кэш первого уровня) — привязан к сессии (
Session). Живёт, пока сессия жива. Умерла сессия — и кэш накрылся медным тазом. Всё, что в нём было, — тлен. - L2 (кэш второго уровня) — это уже статик, блядь, уровень фабрики (
SessionFactory). Один на всех. Сущности там могут пережить не одну сессию, пока их не вытеснят или не инвалидируют.
Как это, сука, настроить?
- Выбрать провайдера. Их, блядь, овердохуища: Ehcache, Infinispan, Hazelcast... Выбирай, что душе угодно, как ту самую ткачиху из сказки.
- Включить в конфиге. Тыкаешь в
persistence.xmlили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>
- Пометить сущности. Вот тут главное — не забыть аннотации, а то будет как с тем немым Герасимом: хочешь сказать, а нихуя не получается.
@Entity
@Cacheable // Говорит: "Эту сущность, блядь, можно кэшировать!"
@org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_WRITE) // А тут стратегию выбираешь
public class Product { ... }
Про стратегии (CacheConcurrencyStrategy) — это отдельная песня:
READ_ONLY: Для данных, которые меняются реже, чем я в спортзал хожу. Только для чтения, идеально для справочников.READ_WRITE: Самый частый гость. Использует мягкие блокировки. Подходит для данных, которые часто читают, но редко пишут. Согласованность есть, но не транзакционная, блядь.NONSTRICT_READ_WRITE: Обновляет кэш уже после коммита транзакции. Могут быть микро-окна, когда в кэше лежит старый хуйня. Но зато быстрее.TRANSACTIONAL: Полная, блядь, паранойя. Строгая транзакционность (JTA). Дорого, сложно, но если нужна абсолютная консистентность — то вот она.
А теперь, внимание, важные нюансы, про которые все забывают, как про ту царевну-лебедь:
- Кэшируются только сущности с
@Id. Запросы (Query) — это отдельная история, для них есть query cache, и его надо включать отдельно, ядрёна вошь! - Самый больной вопрос: а если данные поменяли в обход Hibernate? Напрямую SQL'ем или другое приложение? Кэш-то об этом не узнает и будет выдавать старьё. Тут либо инвалидацию делать, либо стратегии выбирать с учётом этого риска. Иначе будет как в том анекдоте: "И тут я охуел".
Короче, инструмент мощный, но, как и всё у нас на Руси, требует мозгов и понимания, что ты делаешь. А то настроишь READ_ONLY на часто обновляемые данные и потом будешь чесать репу: "Чё за хуйня? Данные не меняются!". В общем, думай, бошка, прежде чем тыкать.