36.Что такое Criteria API и для чего он используется?

36.Что такое Criteria API и для чего он используется?

UNKNOWN

Начиная  с  версии  5.0  собственный  Hibernate  Criteria  API  признан устаревшим  и  не  развивается.  Вместо  него  рекомендуется  использовать  JPA Criteria API.

Начиная  с  версии  5.2  Hibernate  Criteria  API  объявлен  deprecated  и  не рекомендуется к использованию.

Hibernate Criteria API

Это тоже язык запросов, аналогичный JPQL (Java Persistence query language), однако запросы основаны на методах и объектах. Hibernate Criteria API является более объектно-ориентированным  для  запросов,  которые  получают  результат  из  базы данных. Для операций update, delete или других DDL манипуляций использовать Criteria API нельзя. Критерии используются только для выборки из базы данных в более объектно-ориентированном стиле. Используется для динамических запросов. Запросы выглядят так:

session.createCriteria(Person.class)

  .setMaxResults(10)

  .list()

  .forEach(System.out::println);

Запрос выше полностью аналогичен запросу HQL \"from Person\". С Criteria также работают и все те вещи, которые работают и с Query: пейджинг, таймауты и т.д.

Разумеется, в Criteria запросах можно и нужно накладывать условия, по которым объекты будут отбираться:

session.createCriteria(Person.class)

  .add(Restrictions.eq(\"lastName\", \"Testoff\"))

  .list()

  .forEach(System.out::println);

JPA Criteria API

Criteria API - это актуальный API, используемый для определения запросов для сущностей.  Это  альтернативный  способ  определения  JPQL-запроса.  Эти  запросы типобезопасны, переносимы и легко меняются путем изменения синтаксиса.

Основные преимущества JPA Criteria API:

  • ошибки могут быть обнаружены во время компиляции;
  • позволяет  динамически  формировать  запросы  на  этапе  выполнения приложения.

Запросы на основе строк JPQL и запросы на основе критериев JPA одинаковы по производительности и эффективности. 

Для простых статических запросов предпочтительнее использовать строковые запросы JPQL (например, в виде именованных запросов). Для динамических запросов, которые  создаются  во  время  выполнения  -  JPA  Criteria  API  может  быть предпочтительней.  Например,  построение  динамического  запроса  на  основе  полей, которые  пользователь  заполняет  в  рантайме  в  форме,  которая  содержит  много необязательных полей. Ожидается, что построение этого запроса будет более ясным и понятным при использовании JPA Criteria API, поскольку устраняет необходимость в создании запроса с использованием многих операций конкатенации строк. 

Пример использования JPA Criteria API:

EntityManager em = entityManagerFactory.createEntityManager();

em.getTransaction().begin();

CriteriaBuilder cb = em.getCriteriaBuilder();

CriteriaQuery<Person> personCriteria = cb.createQuery(Person.class);

Root<Person> personRoot = personCriteria.from(Person.class);

personCriteria.select(personRoot);

em.createQuery(personCriteria)

  .getResultList()

  .forEach(System.out::println);

Алгоритм: 

  1. создаём  EntityManager, 
  2. открываем  транзакцию  и  создаём CriteriaBuilder,  который  будет  строить  объекты  запросов. 
  3. С  помощью  CriteriaBuilder создаём  CriteriaQuery,  который  параметризуется  типом,  который  этот  запрос возвращает.
  4. Затем создаём корневой объект, от которого производится обход дерева свойств при накладывании ограничений или указании, что выбирать.
  5. Последним шагом говорится, что же мы хотим выбрать
  6. и, наконец, запрос отправляется в EntityManager, где  и  выполняется  как  обычно.  Построенный  выше  весьма  многословный  пример эквивалентен JPQL запросу «from Person».

Все шаги, перечисленные выше, являются обязательными для создания запроса с помощью Criteria API. Важно понимать, что корневой объект указывает JPQL, откуда будут браться данные, а CriteriaQuery указывает тип возвращаемых данных. И типы Root  и CriteriaQuery  могут отличаться:

CriteriaQuery<Passport> passportCriteria = cb.createQuery(Passport.class);

Root<Person> personPassportRoot = passportCriteria.from(Person.class);

passportCriteria.select(personPassportRoot.get(\"passport\"));

em.createQuery(passportCriteria)

  .getResultList()

  .forEach(System.out::println);

Этот  запрос  аналогичен  JPQL  запросу  «select  passport  from  Person»  и показывает,  что  класс,  из  которого  запрашиваются  данные  и  класс,  который  вернёт запрос, могут быть разными.

Metamodel и типобезопасность.

Все примеры выше решают проблему с программным созданием запросов, но всё ещё бессильны перед изменениями сущностей. В самом деле, изменив в сущности Person поле workingPlaces на jobs - развалится этот запрос:

CriteriaQuery<Person> personWorkCriteria = cb.createQuery(Person.class);

Root<Person> personWorkRoot = personWorkCriteria.from(Person.class);

Join<Person, Company> company = personWorkRoot.join(\"workingPlaces\");

personWorkCriteria.select(personWorkRoot);

personWorkCriteria.where(cb.equal(company.get(\"name\"), \"Acme Ltd\"));

em.createQuery(personWorkCriteria)

  .getResultList()

  .forEach(System.out::println);

И мы не узнаем, что он развалится, пока не попробуем его исполнить.

Metamodel решает эту проблему, создавая специальные описательные классы, которые используются в Criteria API вместо имён полей. Сам Metamodel класс выглядит примерно вот так:

@StaticMetamodel(Company.class)

public abstract class Company_ extends AbstractIdentifiableObject_ {

  public static volatile SingularAttribute<Company, String> name;

  public static volatile CollectionAttribute<Company, Person> workers;

}

В Metamodel классе описываются, какие поля присутствуют в сущности, какого они  типа,  коллекция  это  или  нет  и  т.д.  Для  каждой  сущности  создаётся  свой  класс Metаmodel.

Создаются  классы  Metamodel  разумеется  не  вручную.  То  есть  можно  их  и вручную создать, но тогда пропадает автоматичность проверки и теряется смысл всей этой затеи.

Обычно же классы Metamodel генерируются на этапе компиляции тем или иным методом. Конкретная реализация генерации зависит от конкретной реализации JPA и может меняться.


Предыдущий вопрос: 34. Как работать с кэшем 2 уровня?

Следующий вопрос: 37. Расскажите про проблему N+1 Select и путях ее решения.

Все вопросы по теме: список

Все темы: список

Вопросы/замечания/предложения/нашли ошибку: напишите мне

Report Page