До выхода версии JPA 2.0 в спецификации кэширование не упоминалось. Менеджер сущностей является кэшем первого уровня, используемым для всесторонней обработки информации для базы данных, а также для кэширования сущностей, которые живут короткий период времени. Этот кэш первого уровня задействуется отдельно в случае с каждой транзакцией для уменьшения количества SQL-запросов в рамках определенной транзакции. Например, если объект будет модифицирован несколько раз в рамках одной и той же транзакции, то менеджер сущностей сгенерирует только один оператор UPDATE в конце этой транзакции. Кэш первого уровня не является производительным кэшем.
Однако все реализации JPA используют производительный кэш (также называемый кэшем второго уровня) для оптимизации доступа к базам данных, запросов, соединений и т. д. Как показано на рис. 6.3, кэш второго уровня располагается между менеджером сущностей и базой данных с целью уменьшения трафика путем сохранения объектов загруженными в память и доступными для всего приложения.
Рис. 6.3. Кэш второго уровня
Каждая реализация наделяется своим подходом к кэшированию объектов путем либо разработки собственного механизма, либо повторного использования уже существующих решений (с открытым исходным кодом или коммерческих). Кэширование может быть распределено по кластеру либо нет — все возможно, когда спецификация игнорирует соответствующую тему. Создатели JPA 2.0 признали, что кэш второго уровня необходим, и добавили операции кэширования в стандартный API-интерфейс. Приведенный в листинге 6.33 API-интерфейс крайне минималистичен (поскольку цель JPA не заключается в том, чтобы стандартизировать полнофункциональный кэш), однако позволяет коду выполнять запросы к некоторым сущностям и удалять их из кэша второго уровня стандартным образом. Как и менеджер сущностей, javax.persistence.Cache является интерфейсом, реализуемым системой кэширования поставщика постоянства.
public interface Cache {
··// Содержит ли кэш определенную сущность
··public boolean contains(Class cls, Object id);
··// Удаляет сущность из кэша
··public void evict(Class cls, Object id);
··// Удаляет сущности указанного класса (и его подклассы) из кэша
··public void evict(Class cls);
··// Очищает кэш
··public void evictAll();
··// Возвращает реализацию кэша, специфичную для поставщика
··public
}
Вы можете использовать этот API для проверки того, содержится ли определенная сущность в кэше второго уровня, а также для очищения всего кэша. Задействуя этот API, вы можете явным образом проинформировать поставщика постоянства о том, является ли сущность кэшируемой, с помощью аннотации @Cacheable, как показано в листинге 6.34. Если у сущности отсутствует аннотация @Cacheable, то эта сущность и ее состояние не должны кэшироваться поставщиком.
@Entity
@Cacheable(true)
public class Customer {
··@Id @GeneratedValue
··private Long id;
··private String firstName;
··private String lastName;
··private String email;
··// Конструкторы, геттеры, сеттеры
}
Аннотация @Cacheable принимает логическое значение. Как только вы решите, какая сущность должна быть кэшируемой, вам придется проинформировать поставщика о том, какой механизм кэширования следует использовать. Это можно сделать с помощью JPA, указав атрибут shared-cache-mode в файле persistence.xml. Далее приведены возможные значения:
• ALL — будут кэшироваться все сущности, а также связанные с ними состояния и данные;
• DISABLE_SELECTIVE — будут кэшироваться все сущности за исключением тех, что снабжены аннотацией @Cacheable(false);
• ENABLE_SELECTIVE — будут кэшироваться все сущности, снабженные аннотацией @Cacheable(true);
• NONE — кэширование будет отключено для единицы сохраняемости;
• UNSPECIFIED — поведение кэширования будет неопределенным (могут быть применены правила по умолчанию, специфичные для поставщика).