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

Спящий именованный запрос

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

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 — если результаты запроса можно кэшировать, назовите область кеша запросов для использования.
  • comment – комментарий, добавляемый к сгенерированному 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 и собственные запросы.

Исходный код доступен на GitHub .