Какие уровни кэширования существуют в Hibernate?

Ответ

Hibernate предоставляет многоуровневую систему кэширования для минимизации обращений к базе данных.

1. Кэш первого уровня (L1, Session Cache)

  • Включен по умолчанию, привязан к объекту Session (или EntityManager).
  • Обеспечивает гарантию уникальности экземпляра сущности в рамках сессии.
  • Жизненный цикл равен жизненному циклу сессии.
Session session = sessionFactory.openSession();
// Первый вызов -> запрос к БД, результат кладется в L1
User u1 = session.get(User.class, 1L);
// Второй вызов -> данные берутся из L1 кэша
User u2 = session.get(User.class, 1L);
assert u1 == u2; // true
session.close(); // Кэш очищается

2. Кэш второго уровня (L2, Second-Level Cache)

  • Разделяемый кэш между разными сессиями.
  • Требует явной настройки и внешнего провайдера (Ehcache, Infinispan, Hazelcast).
  • Сущность должна быть помечена как кэшируемая.
@Entity
@Cacheable // JPA аннотация
@org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_WRITE) // Стратегия Hibernate
public class Product { ... }

3. Кэш запросов (Query Cache)

  • Кэширует результаты HQL/Criteria/JPA Query вместе с идентификаторами сущностей.
  • Для работы требует включенного L2 кэша.
  • Настраивается в конфигурации Hibernate и указывается для конкретного запроса.
Query query = session.createQuery("from User where active = true");
query.setCacheable(true); // Включение кэширования для этого запроса
List<User> users = query.list();

Применение: Кэширование наиболее эффективно для данных, которые часто читаются и редко изменяются (справочники, конфигурации).

Ответ 18+ 🔞

О, смотри-ка, пришёл очередной джедай, который думает, что его приложение летает, а оно, блядь, ползает, как черепаха в сиропе. А всё потому, что базу дергает на каждый чих! Так, слушай сюда, сейчас я тебе про кэширование в Hibernate расскажу, но так, чтобы ты не просто понял, а прочувствовал всей своей хитрой жопой.

1. Кэш первого уровня (L1, он же Session Cache)

Это твой личный, персональный кошель, сука. Включен всегда, как приложение запустил. Живёт ровно столько, сколько живёт твоя сессия (Session или EntityManager). Его главная фишка — гарантия, что в рамках одной сессии у тебя будет один и тот же объект сущности. Не два разных, а один, блядь!

Session session = sessionFactory.openSession();
// Первый раз — лезем в базу, как честные пацаны. Результат — в L1.
User u1 = session.get(User.class, 1L);
// Второй раз — ага, уже смекаем! Берём из своего кармана (L1), базу не трогаем.
User u2 = session.get(User.class, 1L);
assert u1 == u2; // true, ёпта! Это один и тот же объект, а не клон.
session.close(); // Сессию закрыл — и кэш твой накрылся медным тазом. Всё, пусто.

Вот и вся магия. Ничего сложного, но экономия на ровном месте.

2. Кэш второго уровня (L2, Second-Level Cache)

А вот это уже серьёзная движуха. Это общаг, блядь! Разделяемый кэш для всех сессий приложения. Но он по умолчанию выключен, потому что за всё хорошее надо платить (ну или хотя бы настроить). Требует внешнего провайдера — Ehcache, Infinispan, какой-нибудь Hazelcast. И сущность нужно явно пометить, мол, «я, сука, кэшируемая».

@Entity
@Cacheable // Скажи JPA, что ты не против
@org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_WRITE) // А это Hibernate-у, со стратегией
public class Product { ... }

Теперь, если одна сессия вытащила Product с id=5, вторая сессия может взять его из этого общего кэша, даже не открывая рот на базу данных. Красота, да? Но только для данных, которые редко меняются, а то будет больно и неконсистентно.

3. Кэш запросов (Query Cache)

Самый хитрый, блядь, уровень. Он кэширует не сами объекты, а результаты твоих HQL-запросов — точнее, списки идентификаторов сущностей, которые под него подходят. Но есть нюанс, ёпта: для его работы обязательно должен быть включён L2 кэш. Иначе него работает нихуя.

Настраивается в конфигурации, а для конкретного запроса включается так:

Query query = session.createQuery("from User where active = true");
query.setCacheable(true); // Вот этот волшебный пендель
List<User> users = query.list();

Первый раз выполнится запрос, результаты (idшники) закэшируются. Второй раз — бах! — идём не в базу, а в кэш запросов, достаём оттуда id, а сами сущности уже тянем из L2 кэша (или из базы, если их там нет). Охуенно, правда?

Короче, резюмирую, чтобы ты не проебал: Кэширование — это твой друг, когда данные часто читаются, но меняются редко. Справочники всякие, настройки, конфиги — вот это всё. А если у тебя данные меняются каждую секунду, то кэш только мозги пудрить будет и приводить к пиздецу, когда протухшие данные начнёт показывать. Думай головой, а не жопой.