Не вдаваясь в подробности, вы можете видеть, что у ключевых слов SELECT, FROM и WHERE имеется API-представление с помощью методов select(), from() и where(). Это правило относится ко всем ключевым словам JPQL. Запросы с использованием критериев генерируются благодаря интерфейсу CriteriaBuilder, который получает менеджер сущностей (атрибут em в листингах 6.24 и 6.25). Он включает методы для генерирования определения запроса (этот интерфейс определяет такие ключевые слова, как desc(), asc(), avg(), sum(), max(), min(), count(), and(), or(), greaterThan(), lowerThan()…). Еще одна роль CriteriaBuilder — выступать в качестве главной фабрики запросов с использованием критериев (CriteriaQuery) и элементов запросов с применением критериев. Этот интерфейс определяет такие методы, как select(), from(), where(), orderBy(), groupBy() и having(), которые имеют эквивалентное значение в JPQL. В листинге 6.24 получение псевдонима c (как в SELECT c FROM Customer) осуществляется с помощью интерфейса Root (Root
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery
Root
criteriaQuery.select(c). where(builder.greaterThan(c.get("age"). as(Integer.class), 40));
Query query = em.createQuery(criteriaQuery). getResultList();
List
Обратимся к другому примеру. В листинге 6.25 приведен запрос, который извлекает всех клиентов старше 40 лет. Выражение c.get("age") извлекает атрибут age из сущности Customer и проверяет, больше ли 40 его значение.
Я начал этот раздел, сказав, что Criteria API позволяет вам писать операторы без ошибок. Однако это не совсем так. Взглянув на листинги 6.24 и 6.25, вы тем не менее сможете увидеть строки ("firstName" и "age"), которые представляют атрибуты сущности Customer. Таким образом, опечатки все равно возможны. В листинге 6.25 нам даже потребовалось преобразовать age в Integer ((c.get("age"). as(Integer.class)), поскольку по-другому никак нельзя было бы понять, что атрибут age имеет тип Integer. Для решения этих проблем Criteria API предусматривает класс статической метамодели для каждой сущности, что обеспечивает безопасность типов в данном API-интерфейсе.
Типобезопасный Criteria API. Листинги 6.24 и 6.25 почти типобезопасны: каждое ключевое слово JPQL может быть представлено в методе интерфейсов CriteriaBuilder и CriteriaQuery. Единственной отсутствующей частью являются атрибуты сущности, которые основаны на строках: способ ссылки на атрибут firstName сущности Customer заключается в вызове c.get("firstName"). Метод get принимает строку в качестве параметра. Типобезопасный Criteria API решает проблему, переопределяя этот метод с использованием выражения пути из классов API метамодели, что обеспечивает безопасность типов.
В листинге 6.26 показана сущность Customer с несколькими атрибутами разных типов (Long, String, Integer, Address).
@Entity
public class Customer {
··@Id @GeneratedValue
··private Long id;
··private String firstName;
··private String lastName;
··private Integer age;
··private String email;
··private Address address;
··// Конструкторы, геттеры, сеттеры
}
Для обеспечения безопасности типов JPA 2.1 может генерировать класс статической метамодели для каждой сущности. В соответствии с соглашением каждая сущность X будет обладать классом метаданных с именем X_ (со знаком подчеркивания). Таким образом, у сущности Customer будет свое представление метамодели, описанное в классе Customer_, который приведен в листинге 6.27.