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 возвращает набор всех элементов. Давайте посмотрим, как это происходит:
- Объект
SessionFactory
создает экземплярSession.
- Сессия возвращает экземпляр
CriteriaBuilder
, используя метод getCriteriaBuilder(
).
CriteriaBuilder
использует метод createQuery()
. Это создает объектCriteriaQuery()
, который далее возвращает экземпляр Query.- В конце мы вызываем метод
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 и здесь .