Перейти к основному содержимому

JPA и Hibernate — критерии, JPQL и HQL-запросы

· 6 мин. чтения

1. Обзор

В этом руководстве мы увидим, как использовать запросы JPA и Hibernate, а также разницу между запросами Criteria, JPQL и HQL. Критериальные запросы позволяют пользователю писать запросы без использования необработанного SQL. Наряду с запросами Criteria мы рассмотрим написание именованных запросов Hibernate и способы использования аннотации @Query в Spring Data JPA.

Прежде чем мы углубимся в это, мы должны отметить, что Hibernate Criteria API устарел, начиная с Hibernate 5.2. Поэтому в наших примерах мы будем использовать JPA Criteria API , поскольку это новый и предпочтительный инструмент для написания запросов Criteria. Итак, с этого момента мы будем называть его просто Criteria API.

2. Критериальные запросы

Criteria API помогает создавать объект запроса Criteria, применяя к нему различные фильтры и логические условия. Это альтернативный способ манипулирования объектами и возврата нужных данных из таблицы СУБД.

Метод createCriteria() из сеанса Hibernate возвращает экземпляр объекта сохраняемости для выполнения запроса критериев в приложении. Проще говоря, Criteria API создает запрос критериев, который применяет различные фильтры и логические условия.

2.1. Зависимости Maven

Давайте возьмем последнюю версию эталонной зависимости JPA , которая реализует JPA в Hibernate, и добавим ее в наш pom.xml :

<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.3.2.Final</version>
</dependency>

2.2. Использование запросов и выражений с критериями

В соответствии с условиями пользователя CriteriaBuilder контролирует результаты запроса . Он использует метод where() из CriteriaQuery , который предоставляет выражения CriteriaBuilder .

Давайте посмотрим на объект, который мы будем использовать в этой статье:

public class Employee {

private Integer id;
private String name;
private Long salary;

// standard getters and setters
}

Давайте рассмотрим простой запрос критериев, который извлечет все строки «Сотрудник» из базы данных:

Session session = HibernateUtil.getHibernateSession();
CriteriaBuilder cb = session.getCriteriaBuilder();
CriteriaQuery<Employee> cr = cb.createQuery(Employee.class);
Root<Employee> root = cr.from(Employee.class);
cr.select(root);

Query<Employee> query = session.createQuery(cr);
List<Employee> results = query.getResultList();
session.close();
return results;

Приведенный выше запрос Criteria возвращает набор всех элементов. Давайте посмотрим, как это происходит:

  1. Объект SessionFactory создает экземпляр Session.
  2. Сессия возвращает экземпляр CriteriaBuilder , используя метод getCriteriaBuilder ( ).
  3. CriteriaBuilder использует метод createQuery () . Это создает объект CriteriaQuery() , который далее возвращает экземпляр Query.
  4. В конце мы вызываем метод getResult() для получения объекта запроса, содержащего результаты.

Давайте посмотрим на другое выражение из CriteriaQuery :

cr.select(root).where(cb.gt(root.get("salary"), 50000));

В качестве результата приведенный выше запрос возвращает набор сотрудников с окладом более 50000.

3. JPQL

JPQL расшифровывается как Java Persistence Query Language. Spring Data предоставляет несколько способов создания и выполнения запроса, и JPQL — один из них. Он определяет запросы с использованием аннотации @Query в Spring для выполнения как JPQL, так и собственных запросов SQL. В определении запроса по умолчанию используется JPQL.

Мы используем аннотацию @Query для определения SQL-запроса в Spring. Любой запрос, определяемый аннотацией @Query , имеет более высокий приоритет по сравнению с именованными запросами , которые аннотируются с помощью @NamedQuery .

3.1. Использование запросов JPQL

Давайте построим динамический запрос, используя JPQL:

@Query(value = "SELECT e FROM Employee e")
List<Employee> findAllEmployees(Sort sort);

С запросами JPQL, имеющими параметры-аргументы, Spring Data передает аргументы метода в запрос в том же порядке, что и объявление метода. Давайте рассмотрим пару примеров, в которых аргументы метода передаются в запрос:

@Query("SELECT e FROM Employee e WHERE e.salary = ?1")
Employee findAllEmployeesWithSalary(Long salary);
@Query("SELECT e FROM Employee e WHERE e.name = ?1 and e.salary = ?2")
Employee findEmployeeByNameAndSalary(String name, Long salary);

Для последнего вышеприведенного запроса аргумент метода имени передается в качестве параметра запроса относительно индекса 1, а аргумент зарплаты передается в качестве параметра запроса индекса 2.

3.2. Использование собственных запросов JPQL

Мы можем выполнять эти SQL-запросы непосредственно в наших базах данных, используя собственные запросы, которые ссылаются на реальные базы данных и табличные объекты. Нам нужно установить значение атрибута nativeQuery в true для определения собственного SQL-запроса . Собственный SQL-запрос будет определен в атрибуте value аннотации.

Давайте посмотрим на собственный запрос, который показывает индексированный параметр, который нужно передать в качестве аргумента для запроса:

@Query(
value = "SELECT * FROM Employee e WHERE e.salary = ?1",
nativeQuery = true)
Employee findEmployeeBySalaryNative(Long salary);

Использование именованных параметров упрощает чтение запроса и делает его менее подверженным ошибкам в случае рефакторинга. Давайте посмотрим на иллюстрацию простого именованного запроса в JPQL и собственном формате:

@Query("SELECT e FROM Employee e WHERE e.name = :name and e.salary = :salary")
Employee findEmployeeByNameAndSalaryNamedParameters(
@Param("name") String name,
@Param("salary") Long salary);

Параметры метода передаются в запрос с использованием именованных параметров. Мы можем определить именованные запросы, используя аннотацию @Param внутри объявления метода репозитория. В результате аннотация @Param должна иметь строковое значение, совпадающее с соответствующим именем запроса JPQL или SQL. ``

@Query(value = "SELECT * FROM Employee e WHERE e.name = :name and e.salary = :salary", 
nativeQuery = true)
Employee findUserByNameAndSalaryNamedParamsNative(
@Param("name") String name,
@Param("salary") Long salary);

4. Высокий уровень качества

HQL расшифровывается как язык запросов Hibernate. Это объектно-ориентированный язык, похожий на SQL , который мы можем использовать для запросов к нашей базе данных. Однако главный недостаток — нечитаемость кода. Мы можем определить наши запросы как именованные запросы, чтобы поместить их в фактический код, который обращается к базе данных.

4.1. Использование именованного запроса Hibernate

Именованный запрос определяет запрос с предопределенной неизменной строкой запроса. Эти запросы безотказны, поскольку они проверяются во время создания фабрики сеансов. Давайте определим именованный запрос, используя аннотацию org.hibernate.annotations.NamedQuery :

@NamedQuery(name = "Employee_FindByEmployeeId",
query = "from Employee where id = :id")

Каждая аннотация @NamedQuery присоединяется только к одному классу сущностей. Мы можем использовать аннотацию @NamedQueries , чтобы сгруппировать более одного именованного запроса для сущности:

@NamedQueries({
@NamedQuery(name = "Employee_findByEmployeeId",
query = "from Employee where id = :id"),
@NamedQuery(name = "Employee_findAllByEmployeeSalary",
query = "from Employee where salary = :salary")
})

4.2. Хранимые процедуры и выражения

В заключение, мы можем использовать аннотацию @NamedNativeQuery для хранения процедур и функций:

@NamedNativeQuery(
name = "Employee_FindByEmployeeId",
query = "select * from employee emp where id=:id",
resultClass = Employee.class)

5. Преимущества запросов Criteria по сравнению с запросами HQL и JPQL

Главное преимущество Criteria Queries перед HQL — приятный, чистый, объектно-ориентированный API . В результате мы можем обнаружить ошибки в Criteria API во время компиляции.

Кроме того, запросы JPQL и запросы Criteria имеют одинаковую производительность и эффективность.

Критериальные запросы более гибкие и обеспечивают лучшую поддержку написания динамических запросов по сравнению с HQL и JPQL.

Но HQL и JPQL обеспечивают встроенную поддержку запросов, которая невозможна с запросами Criteria. Это один из недостатков запроса Criteria.

Мы можем легко писать сложные соединения, используя собственные запросы JPQL , тогда как ими сложно управлять, применяя то же самое с Criteria API.

6. Заключение

В этой статье мы в основном рассмотрели основы запросов Criteria, запросов JPQL и запросов HQL в Hibernate и JPA. Кроме того, мы узнали, как использовать эти запросы, а также преимущества и недостатки каждого подхода.

Как всегда, полные примеры кода, используемые в этой статье, можно найти на GitHub и здесь .