«1. Обзор

Основным недостатком разбросанных по объектам доступа к данным HQL и SQL является то, что код становится нечитаемым. Следовательно, может иметь смысл сгруппировать все HQL и SQL в одном месте и использовать только их ссылку в фактическом коде доступа к данным. К счастью, Hibernate позволяет нам делать это с помощью именованных запросов.

Именованный запрос — это статически определенный запрос с предопределенной неизменной строкой запроса. Они проверяются при создании фабрики сеансов, что приводит к быстрому сбою приложения в случае ошибки.

В этой статье мы увидим, как определять и использовать именованные запросы Hibernate с помощью аннотаций @NamedQuery и @NamedNativeQuery.

2. Сущность

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

@Entity
public class DeptEmployee {
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE)
    private long id;

    private String employeeNumber;

    private String designation;

    private String name;

    @ManyToOne
    private Department department;

    // getters and setters
}

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

3. Именованный запрос

Чтобы определить это как именованный запрос, мы будем использовать аннотацию org.hibernate.annotations.NamedQuery. Он расширяет javax.persistence.NamedQuery функциями Hibernate.

Мы определим его как аннотацию класса DeptEmployee:

@org.hibernate.annotations.NamedQuery(name = "DeptEmployee_findByEmployeeNumber", 
  query = "from DeptEmployee where employeeNumber = :employeeNo")

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

Если у нас есть более одного именованного запроса для объекта, мы будем использовать аннотацию @NamedQueries для их группировки:

@org.hibernate.annotations.NamedQueries({
    @org.hibernate.annotations.NamedQuery(name = "DeptEmployee_FindByEmployeeNumber", 
      query = "from DeptEmployee where employeeNumber = :employeeNo"),
    @org.hibernate.annotations.NamedQuery(name = "DeptEmployee_FindAllByDesgination", 
      query = "from DeptEmployee where designation = :designation"),
    @org.hibernate.annotations.NamedQuery(name = "DeptEmployee_UpdateEmployeeDepartment", 
      query = "Update DeptEmployee set department = :newDepartment where employeeNumber = :employeeNo"),
...
})

Обратите внимание, что запрос HQL может быть операцией в стиле DML. Таким образом, это не обязательно должен быть только оператор select. Например, у нас может быть запрос на обновление, как в DeptEmployee_UpdateEmployeeDesignation выше.

3.1. Настройка функций запроса

Мы можем установить различные функции запроса с помощью аннотации @NamedQuery. Давайте рассмотрим пример:

@org.hibernate.annotations.NamedQuery(
  name = "DeptEmployee_FindAllByDepartment", 
  query = "from DeptEmployee where department = :department",
  timeout = 1,
  fetchSize = 10
)

Здесь мы настроили интервал времени ожидания и размер выборки. Помимо этих двух, мы также можем установить такие функции, как:

    cacheable — кэшируется ли запрос (результаты) или нет cacheMode — режим кэширования, используемый для этого запроса; это может быть GET, IGNORE, NORMAL, PUT или REFRESH cacheRegion — если результаты запроса можно кэшировать, назовите область кэша запроса для использования комментария — комментарий, добавляемый к сгенерированному SQL-запросу; предназначен для администраторов баз данных flushMode — режим сброса для этого запроса, один из ALWAYS, AUTO, COMMIT, MANUAL или PERSISTENCE_CONTEXT

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

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

Query<DeptEmployee> query = session.createNamedQuery("DeptEmployee_FindByEmployeeNumber", 
  DeptEmployee.class);
query.setParameter("employeeNo", "001");
DeptEmployee result = query.getSingleResult();

Здесь мы использовали метод createNamedQuery. Он принимает имя запроса и возвращает объект org.hibernate.query.Query.

4. Именованный собственный запрос

Помимо запросов HQL, мы также можем определить собственный SQL как именованный запрос. Для этого мы можем использовать аннотацию @NamedNativeQuery. Хотя он похож на @NamedQuery, он требует немного большей настройки.

Давайте рассмотрим эту аннотацию на примере:

@org.hibernate.annotations.NamedNativeQueries(
    @org.hibernate.annotations.NamedNativeQuery(name = "DeptEmployee_GetEmployeeByName", 
      query = "select * from deptemployee emp where name=:name",
      resultClass = DeptEmployee.class)
)

Так как это нативный запрос, мы должны указать Hibernate, какой класс сущностей сопоставить с результатами. Следовательно, для этого мы использовали свойство resultClass.

Другой способ сопоставить результаты — использовать свойство resultSetMapping. Здесь мы можем указать имя предопределенного SQLResultSetMapping.

Обратите внимание, что мы можем использовать только один из классов resultClass и resultSetMapping.

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

Чтобы использовать именованный собственный запрос, мы можем использовать Session.createNamedQuery():

Query<DeptEmployee> query = session.createNamedQuery("DeptEmployee_FindByEmployeeName", DeptEmployee.class);
query.setParameter("name", "John Wayne");
DeptEmployee result = query.getSingleResult();

Или Session.getNamedNativeQuery():

NativeQuery query = session.getNamedNativeQuery("DeptEmployee_FindByEmployeeName");
query.setParameter("name", "John Wayne");
DeptEmployee result = (DeptEmployee) query.getSingleResult();

Единственная разница между этими два подхода — это возвращаемый тип. Второй подход возвращает NativeQuery, который является подклассом Query.

5. Хранимые процедуры и функции

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

@org.hibernate.annotations.NamedNativeQuery(
  name = "DeptEmployee_UpdateEmployeeDesignation", 
  query = "call UPDATE_EMPLOYEE_DESIGNATION(:employeeNumber, :newDesignation)", 
  resultClass = DeptEmployee.class)

«

«Обратите внимание, что хотя это запрос на обновление, мы использовали свойство resultClass. Это связано с тем, что Hibernate не поддерживает чисто собственные скалярные запросы. И способ обойти эту проблему — либо установить resultClass, либо resultSetMapping.

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

В этой статье мы увидели, как определять и использовать именованные HQL и нативные запросы.