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 .