Язык запросов 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 users = query.list();
User представляет POJO класс (User.java), который ассоциирован с таблицей в БД.
WHERE
Оператор WHERE накладывает условие на выборку определенных записей из БД. В следующем коде оператор WHERE используется точно также, как и в обычном SQL :
Query query = session.createQuery("FROM User where name = 'Иван'"); List 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 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 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)
В консоль будут выведены следующие сформированные Hibernate запросы удаления сущности User и связанных с ней сущностей Auto :
Hibernate: delete from autos where aid=? Hibernate: delete from autos where aid=? Hibernate: delete from USERS where >Обратите внимание, что дочерние/связанные сущности удаляются в первую очередь.
Использование алиаса 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();
Вставка данных в таблицу
Мы разобрались, как создавать таблицы, давай теперь детально рассмотрим, как добавлять данные в таблицу с помощью SQL-запроса.
На самом деле это делать очень просто, но есть несколько нюансов. Самый простой вариант вставки данных в таблицу требует указания двух вещей:
Имена колонок нужно указывать обязательно, так как очень часто колонки имеют значения по умолчанию, которые не указывают при вставке данных. И обычно в каждой таблице есть хотя бы одна такая колонка, например, id строки.
Общий вид запроса вставки данных в таблицу выглядит так:
INSERT INTO таблица (колонка1, колонка2, колонка3) VALUES (значение1, значение2, значение3), (значение1, значение2, значение3), (значение1, значение2, значение3);
Например, ты хочешь вставить в таблицу user новую запись, вот как будет выглядеть такой запрос:
INSERT INTO user (name, level, created_time) VALUES (‘Рабинович’, 5, ‘2022-06-06’);
Оператор INSERT INTO SELECT
Еще один частый сценарий вставки данных в таблицу — это взять их из другой таблицы, схемы и даже СУБД.
Для этого есть другой формат запроса INSERT INTO, у которого вместо части VALUES можно указать запрос на выборку данных.
Общий вид такого запроса имеет вид:
INSERT INTO таблица (колонка1, колонка2, колонка3) SELECT-запрос;
Давай напишем запрос, с помощью которого добавим всех пользователей из таблицы employee в таблицу user:
INSERT INTO user (name, created_time) SELECT employee.name, employee.join_date FROM employee;
У нас в таблице employee есть различные данные, но из них мы выбираем только два поля – имя и время прихода в компанию.
Так же таблица user требует указать ей уровень пользователя – level. В таблице employee у сотрудников нет уровня, поэтому мы воспользуемся тем, что у таблицы user у поля level есть значение по умолчанию. Мы просто не будем указывать level, и SQL установит значение по умолчанию.
Допустим, нас не устраивает значение по умолчанию, и мы хотим, чтобы level был 99, а user.created_time заменить на сегодняшнюю дату, тогда можно написать так:
INSERT INTO user (name, level, created_time) SELECT employee.name, 99, CURDATE() FROM employee;
Можно наворотить еще кучу всего интересного, но, думаю, хватит пока и этого. Подробнее можно почитать на официальный странице MySQL.