1. Навіщо потрібний кеш запитів

Перепишемо наш приклад із отриманням співробітників на HQL:


Employee director1 = session.createQuery("from Employee where id = 4").uniqueResult();
Employee director2 = session.createQuery("from Employee where id = 4").uniqueResult();
 
assertTrue(director1! = director2);

Результати такого типу запитів не зберігаються кешем ні першого, ні другого рівня.

Це саме те місце, де можна використовувати кеш запитів. Його теж за замовчуванням вимкнено. Для включення потрібно додати наступний рядок до конфігураційного файлу:

<property name="hibernate.cache.use_query_cache" value="true"/>

Але це лише половина рішення. Кеш запитів ми включили, але нам потрібно ще й вказати, результати яких запитів ми хочемо кешувати. Це потрібно прописати у запиті Query:


Query query = session.createQuery("from Employee where id = 4");
query.setCacheable(true);
Employee director1 = query.uniqueResult();

Кеш запитів нагадує кеш другого рівня. Але, на відміну від нього, тут ключем до даних кешу є не ідентифікатор об'єкта, а сукупність параметрів запиту. А самі дані — це ідентифікатори об'єктів, які відповідають критеріям запиту. Таким чином, цей кеш раціонально використовувати з кешем другого рівня.

2. Очищення кешу

Одне з важливих завдань під час роботи з кешем — це стежити за тим, щоб об'єкти, що кешуються, змінилися, і видаляти їх з кеша (або оновлювати). Hibernate робить це дуже успішно. Іноді навіть здається, що він керується правилом “у будь-якій незрозумілій ситуації очищай кеш”.

Припустимо, ти хочеш оновити дані користувача через HQL:


Query query = session.createQuery("update Employee set name='Alex' where id = 4")
query. executeUpdate();

Hibernate не може точно знати, що в базі змінилося, але він знає, що ти змінюєш об'єкт типу Employee. Тому після виконання цього запиту Hibernate вилучить зі свого кеша взагалі всі об'єкти типу Employee.

Але ще цікавіше працює NativeQuery:


Query nativeQuery = session.createNativeQuery("update employee set name='Alex' where id = 4")
nativeQuery.executeUpdate();

Виконався рідний SQL-запит до бази даних. Отже, в базі даних щось змінилося — запит викликано в методі executeUpdate(). Тому в цьому випадку Hibernate трохи перестрахується і видалить зі свого кеша взагалі всі об'єкти всіх типів.

Як тобі таке? Ти викликаєш безневинний запит, а Hibernate у відповідь стирає всі дані з кеша! Це, звісно, краще, ніж якби він зберігав у себе об'єкти, які відрізняються від бази, але таке!

Тому творці Hibernate швидко вигадали, як допомогти Hibernate у цьому випадку. Ти можеш вказати йому, який тип сутностей потрібно видалити з кеша:


Query nativeQuery = session.createNativeQuery("update employee set name='Alex' where id = 4");
nativeQuery.unwrap(org.hibernate.SQLQuery.class).addSynchronizedEntityClass(Employee.class);
nativeQuery.executeUpdate();
Примітка. Нативні select-запити не скидають кеш, тільки insert, update, delete, виклики процедур тощо.

3. Ручне очищення кешу

З певних причин ти можеш захотіти видалити об'єкт із кешу самостійно. Зробити це можна у різний спосіб.

Примітка. Об'єкти в кеші зберігаються групами, які називаються регіонами. За замовчуванням ім'я регіону збігається з іменем класу. Тому якщо у тебе є об'єкти типу com.javarush.Employee, всі вони будуть збережені до групи (регіон) з ім'ям “com.javarush.employee”.

>

Якщо ти хочеш отримати доступ до кешу і щось з ним зробити, то це можна зробити за допомогою об'єкта SessionFactory та методу getCache():


session.getSessionFactory().getCache().evictQueryRegion("com.javarush.employee”);

Якщо ти хочеш видалити дані з усіх груп (регіонів), то потрібно виконати такий запит:


session.getSessionFactory().getCache().evictAllRegions();

Щоб видалити один об'єкт із кеша, потрібно передати його ім'я (тип) та id. Зробити це можна двома способами:


session.getSessionFactory().getCache().evictEntityData("Employee", 4);
 
session.getSessionFactory().getCache().evictEntityData(com.javarush.Employee.class, 4);

Також ти можеш перевірити, чи міститься певний об'єкт у кеші:


session.getSessionFactory().getCache().containsEntity("Employee", 4);
session.getSessionFactory().getCache().containsEntity(com.javarush.Employee.class, 4);