Использование JPA Criteria в Spring Data Jpa

Criteria API это мощный механизм по генерации динамических и типобезопасных (при использовании Metamodel) запросов, который напрямую поддерживается в Data Jpa, тем самым снимая ограничения других подходов к описанию запросов, но оставляя при это необходимый минимум автоматизации и автоматической генерации кода.

Код примеров ниже основан на коде из статьи Hello, Spring Data JPA

Спецификации и их исполнение

Spring Data JPA определяет интерфейс Specification для создания таких предикатов Criteria API, которые можно было бы использовать повторно. Интерфейс определяет ровно один метод, который должен вернуть предикат:

Тип T  в данном случае указывает, к какой сущности относится спецификация.

Сама по себе спецификация не очень полезна, а чтобы использовать её с репозиториями, необходимо чтоб репозиторий имел в списке предков интерфейс JpaSpecificationExecutor<T> , где T — тип сущности, с которой работает репозиторий. При этом, даже если планируется работать только с запросами по примеру, наличие в предках интерфейса Repository или любого из его наследников всё равно является обязательным.

Интерфейс JpaSpecificationExecutor<T> определяет несколько методов:

  • T FindOne(Predicate) — возвращает один объект, соответствующий условия
  • Iterable<T> findAll(Predicate) — возвращает несколько объектов, соответствующих условию. Обратите внимание, что возвращается всегда Iterable<T>, без возможности уточнить тип
  • long count(Predicate) — возвращает количество объектов в базе данных, соответствующих условию
  • boolean exists(Predicate) — сообщает, есть ли в базе данных объект соответствующий условию

Все методы принимают объект класса Predicate, содержащий в себе условия запроса.

Пишем спецификации

Можно реализовывать класс Specification явно, но чаще используются вспомогательные классы, которые группируют различные реализации Specifation и предоставляют удобные методы для обращения к ним:

Увы, вспомогательной писанины довольно много, поэтому повторное использование кода становится важной вещью при работе со спецификациями. Впрочем, часть кода можно сократить, использовав лямбды:

Поскольку речь идёт о прямом использовании Jpa Criteria API, сложность и гибкость спецификаций может быть сколь угодно высокой:

Использование спецификаций

Достаточно передать спецификацию в какой либо метод JpaSpecificationExecutor:

При необходимости можно создавать спецификации и по месту использования, например для комбинирования уже существующих:

Код примера доступен на github