Язык запросов HQL

HQL (Hibernate Query Language) – это объекто-ориентированный язык запросов, который очень похож на SQL. Главное различие языков HQL и SQL связано с тем, что SQL формирует запросы из наименований таблиц в базе данных и их столбцов, а HQL работает с сущностями (классами) и их полями (аттрибутами класса).

Hibernate транслирует HQL–запросы в понятные для БД SQL–запросы, которые и выполняют необходимые действия в БД.

Рассмотрим основные ключевые операторы языка HQL :

FROM

Оператор FROM используется для загрузки (чтения) набора объектов. Пример кода :

String hql = "FROM User";
Query query = session.createQuery(hql);
List<User> users = query.list();

User представляет POJO класс (User.java), который ассоциирован с таблицей в БД.

WHERE

Оператор WHERE накладывает условие на выборку определенных записей из БД. В следующем коде оператор WHERE используется точно также, как и в обычном SQL :

Query query = session.createQuery("FROM User where name = 'Иван'");
List<User> users = query.list();

Hibernate может использовать оператор WHERE с именованными параметрами (Named Parameters), определяя значение в режиме run-time. Для подстановки соответствующего значения в запрос используется метод setParameter объекта Query, которому в качестве параметров необходимо передать значения :

String hql = "FROM User where name = :paramName";
Query query = session.createQuery(hql);
query.setParameter("paramName", "Alex");
List<User> users = query.list();

Запросы HQL не чувствительны к регистру операторов, за исключением названий Java классов и их свойств. Т.е. 'SeLect' будет эквивалентен 'Select'. Но вот с наименованием сущностей/классов и их полями это не проходит; они должны соответствовать описаниям.

INSERT

В HQL поддерживается только форма INSERT INTO ... SELECT ... , которая имеет серьезные ограничения для вставки записей. С точки зрения SQL данная форма позволяет вставить одну или несколько записей в таблицу из запроса SELECT ... . Но это характерно для SQL, а Hibernate (HQL) работает только с сущностями. Попробуем использовать данную форму запроса для вставки записи в Hibernate, и рассмотрим следующий код вставки записи пользователя :

String hql = "insert into User (login, name) " +
                "select 'oleg', 'Олег' from User";
int rows = session.createQuery (hql).executeUpdate();
System.out.println("rows : " + rows);

В коде выполняется попытка сохранения сущности User со значениями login='oleg' и name='Олег'. Структуру таблицы и описание сущности можно увидеть в примере связанных сущностей, который описан здесь. Выполнение данного кода выведет в консоль следующую информацию :


Hibernate: 
    insert into USERS ( id, login, name ) 
        select SEQ_USER.nextval,
              'oleg' as col_0_0_, 
              'Олег' as col_1_0_
            from USERS user0_
rows : 2
 

Hibernate сформировал запрос insert и вставил 2 записи. Если посмотреть содержимое таблицы БД, то там можно будет увидеть новые две записи. Полагаю, что вторая запись лишняя, но как от нее избавиться. Если вместо наименования сущности User в первой или во второй позиции подставить наименование таблицы Users, то Hibernate вызовет QuerySyntaxException :


Exception in thread "main" \
        org.hibernate.hql.internal.ast.QuerySyntaxException: \
        Users is not mapped [ \
            insert into Users(login, name) \
                select 'oleg', 'Олег' from net.common.model.User]
 

Таким образом, можно заключить, что вставку записей в таблицы при использовании HQL лучше выполнять с применением сущностей и стандартных методов save или saveUpdate объекта сессии, как это представлено в примере.

UPDATE

Ключевое слово UPDATE используется для обновления одного или нескольких полей объекта. При формировании SQL-запроса можно использовать параметры (Named Parameters), рассмотренные выше. Следующий код демонстрирует динамическое определение параметров при обновлении записи. Результат выполнения запроса — количество обновленных (затронутых) записей :

String hql = "update Contact "
               + "SET firstName = :name "
               +   ", lastName  = :lastName "
               +   ", date      = :dateParam "
               +  " where id = :idParam"
Query query = session.createQuery(hql);

query.setParameter("idParam"  , 48);
query.setParameter("name"     , "Киса");
query.setParameter("lastName" , "Воробьянинов");
query.setParameter("dateParam", new Date());
 
int result = query.executeUpdate();

DELETE

Ключевое слово DELETE используется для удаления одного или нескольких объектов из таблицы. Пример использования :

String hql = "DELETE User WHERE login = :lg";
Query query = session.createQuery(hql);
query.setParameter("lg", "oleg");
int rows = query.executeUpdate();

Представленный выше код будет выполнен, если сущность User не имеет связанных объектов, т.е. из таблицы БД будет удалена сущность. Но, если к примеру, User имеет один или несколько связанных объектов Autos, как это представлено в примере связанных сущностей, то сервер БД не сможет удалить запись из таблицы Users, поскольку в таблице Autos имеются «ссылочные» записи. В этом случае Hibernate вызовет org.hibernate.exception.ConstraintViolationException : could not execute statement.

Для удаления связанных сущностей необходимо использовать объект сессии Session. Следующий код демонстрирует удаление пользователя User, у которого имеются связанные объекты (сущности):

User user = session.load(User.class, 50);
if (user != null) {
    session.delete(user);
}

В консоль будут выведены следующие сформированные Hibernate запросы удаления сущности User и связанных с ней сущностей Auto :


Hibernate: delete from autos where aid=?
Hibernate: delete from autos where aid=?
Hibernate: delete from USERS where id=?
 

Обратите внимание, что дочерние/связанные сущности удаляются в первую очередь.

Использование алиаса AS

Оператор AS можно использовать в качестве алиаcа в HQL запросе, особенно, если запрос получается длинным. Следующий код демонстрирует определение алиаса пользователя User как 'u' (без его использования в запросе) :

String hql = "FROM User AS u";
Query query = session.createQuery(hql);
List results = query.list();

Оператор AS можно не включать в запрос, и сразу же после наименования сущности определить ее алиас :

String hql = "FROM User u";
Query query = session.createQuery(hql);
List results = query.list();

Ниже представлены примеры кода с использованием алиаса.

GROUP BY

При использовании в HQL агрегатных функций необходимо выполнять группировку по определенным в запросе полям, аналогично SQL. Следующий код демонстрирует использование агрегатной функции SUM с группировкой по полю firstName сущности Employee :

String hql = "SELECT SUM (e.salary), e.firtName "
             +      "FROM Employee e "
             +          "GROUP BY e.firstName";
Query query = session.createQuery(hql);
List results = query.list();

Агрегатные функции HQL

В таблице представлены агрегатные функции, поддерживаемые HQL :

ФункцияОписание
avg (property name) получение среднего значения
count (property name or *) получение количества записей
max (property name) получение максимального значения
min (property name) получение минимального значения
sum (property name) вычисление суммарного значения

Использование оператора DISTINCT позволяет выделить уникальные значения. Следующий запрос вернет количество уникальных имен сотрудников :

String hql = "SELECT COUNT (distinct e.firstName) "
              + "FROM Employee e";
Query query = session.createQuery(hql);
List results = query.list();

ORDER BY

Сортировка результатов запроса в HQL выполняется с использованием ORDER BY аналогично SQL. Сортировать значения можно как по возрастанию (ASC ascending), так и по убыванию (DESC descending). Следующий код демонстрирует чтение сотрудников с сортировкой по убыванию :

String hql = "FROM Employee e "
             +  "WHERE e.id > 10 ORDER BY e.salary DESC";
Query query = session.createQuery(hql);
List results = query.list();

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

String hql = "FROM Employee e "
             +  "WHERE e.id > 10 "
             +     "ORDER BY e.firstName DESC, e.salary DESC";
Query query = session.createQuery(hql);
List results = query.list();

Разбиение на страницы

Объект Query включает два метода, позволяющие организовать разбиение данных по-страницам :

  • setFirstResult (int start) — параметр start определяет первую извлекаемую запись в запросе (отсчет от 0);
  • setMaxResults (int max) — параметр max определяет количество извлекаемых записей в результирующем запросе.

Используя вышеописанные методы можно организовать постраничное представление набора данных. Следующий пример выбирает из БД 8 сотрудников, начиная с 5-ой записи :

String hql = "FROM Employee";
Query query = session.createQuery(hql);
query.setFirstResult(5);
query.setMaxResults (8);
List results = query.list();
  Рейтинг@Mail.ru