Этот урок Grails научит основам использования HQL. Grails поддерживает динамические искатели, что делает удобным выполнение простых запросов к базе данных. Но для более сложных случаев Grails предоставляет как Criteria API, так и HQL. Этот урок будет сосредоточен на последнем.
Вступление
Хорошо известно, что Grails находится поверх Spring и Hibernate — двух самых популярных сред Java. Hibernate используется в качестве базовой технологии для объектно-реляционного отображения Grails (GORM).
Hibernate не зависит от базы данных. Это означает, что поскольку Grails основан на нем, мы могли бы писать приложения, совместимые с большинством популярных баз данных. Нам не нужно писать разные запросы для каждой возможной базы данных.
Самый простой способ выполнить запросы к базе данных — через динамические искатели. Это просто и очень интуитивно понятно. Проверьте мой предыдущий пост на учебник по этой теме. Динамические искатели, однако, очень ограничены. Это может не подходить для сложных требований и случаев, когда разработчику требуется более низкий уровень контроля. HQL — очень хорошая альтернатива, поскольку она очень похожа на SQL.
HQL полностью объектно-ориентирован и понимает наследование, полиморфизм и ассоциацию. Его использование обеспечит очень мощный и гибкий API, сохраняя при этом ваше приложение независимым от базы данных. В Grails есть два доменных метода для вызова HQL
- executeQuery — выполняет запросы HQL (операции SELECT)
 - executeUpdate — обновляет базу данных с помощью операций в стиле DML (UPDATE и DELETE).
 
ExecuteQuery
Вот пример класса домена, из которого мы будем запрашивать:
| 
 1 
2 
3 
4 
5 
6 
7 
8 
 | 
package asia.grails.testclass Person {    String firstName    String lastName    int age    static constraints = {    }} | 
Получить все объекты домена
Это код для извлечения всех объектов Person из базы данных:
| 
 1 
 | 
def listOfAllPerson = Person.executeQuery("from Person") | 
Заметь:
- executeQuery — это метод класса Domain, используемый для получения информации (операторы SELECT)
 - Как и в SQL, требуется идентификатор from
 -   Вместо указания таблицы мы указываем класс домена сразу после ключевого слова from .  Мы могли бы также написать запрос так
1
def listOfAllPerson = Person.executeQuery("from asia.grails.test.Person") - Действительно, не указывать предложение select . По умолчанию он возвращает экземпляры объектов указанного класса Domain. В этом примере он вернет список всех объектов Person.
 
Вот пример кода того, как мы могли бы использовать результат
| 
 1 
2 
3 
4 
5 
 | 
listOfAllPerson.each { person ->    println "First Name = ${person.firstName}"    println "Last Name = ${person.lastName}"    println "Age = ${person.age}"} | 
Поскольку listOfAllPerson является списком экземпляров Person, мы можем выполнить итерацию по нему и распечатать детали.
Выберите пункт
Когда предложение select используется явно, HQL не возвращает список объектов домена. Вместо этого он вернет двумерный список. Вот пример, предполагающий, что по крайней мере 1 запись находится в базе данных:
| 
 1 
2 
3 
4 
5 
6 
 | 
def list = Person.executeQuery("select firstName, lastName from Person")def firstPerson = list[0]def firstName = firstPerson[0]def lastName = firstPerson[1]println "First Name = ${firstName}"println "Last Name = ${lastName}" | 
Переменному списку будет присвоен список элементов. Каждый элемент представляет собой список, который соответствует значению, указанному в предложении select.
Код также может быть написан так, чтобы помочь визуализировать структуру данных:
| 
 1 
2 
3 
4 
5 
 | 
def list = Person.executeQuery("select firstName, lastName from Person")def firstName = list[0][0]def lastName = list[0][1]println "First Name = ${firstName}"println "Last Name = ${lastName}" | 
Где пункт
Как и в SQL, мы можем фильтровать результаты запроса, используя предложение where. Вот некоторые примеры:
Люди с фамилией Доу
| 
 1 
 | 
def peopleWithSurnameDoe = Person.executeQuery("from Person where lastName = 'Doe'") | 
Люди, которым исполнилось 18 лет
| 
 1 
 | 
def adults = Person.executeQuery("from Person where age >= 18") | 
Люди, чье имя содержит Джон
| 
 1 
 | 
def peopleWithFirstNameLikeJohn = Person.executeQuery("from Person where firstName like '%John%'") | 
Групповое предложение
Групповое предложение также допускается. Поведение похоже на SQL. Вот пример:
| 
 1 
2 
3 
4 
5 
6 
 | 
def list = Person.executeQuery("select age, count(*) from Person group by age")list.each { item ->    def age = item[0]    def count = item[1]    println "There are ${count} people with age ${age} years old"} | 
Это напечатает все возрасты, найденные в таблице, и сколько людей имеют этот возраст.
Имея пункт
Имеющее предложение полезно отфильтровать результат группы по. Вот пример:
| 
 1 
2 
3 
4 
5 
6 
7 
 | 
def list = Person.executeQuery(    "select age, count(*) from Person group by age having count(*) > 1")    list.each { item ->        def age = item[0]        def count = item[1]    println "There are ${count} people with age ${age} years old"} | 
Это напечатает все возрасты, найденные в таблице, и сколько людей имеют этот возраст, при условии, что в возрастной группе более 1 человека.
пагинация
Производительность не позволяет одновременно извлечь все записи в таблице. Это более эффективно, чтобы страница результатов. Например, получить 10 записей одновременно. Вот пример кода о том, как это сделать:
| 
 1 
2 
3 
 | 
def listPage1 = Person.executeQuery("from Person order by id", [offset:0, max:10])def listPage2 = Person.executeQuery("from Person order by id", [offset:10, max:10])def listPage3 = Person.executeQuery("from Person order by id", [offset:20, max:10]) | 
Параметр max информирует GORM о необходимости выбрать максимум 10 записей. Смещение означает, сколько записей пропустить, прежде чем читать первый результат.
- На странице 1 мы не пропускаем ни одной записи и получаем первые 10 результатов
 - На странице 2 мы пропускаем первые 10 записей и получаем с 11 по 20 записи.
 - На странице 3 мы пропускаем первые 20 записей и получаем 21-30 записи.
 
GORM / Hibernate переведет информацию о подкачке в правильный синтаксис SQL в зависимости от базы данных.
Примечание. Как правило, лучше разбивать предложения по предложениям при разбивке на страницы, в противном случае большая часть базы данных не дает никаких гарантий относительно того, как записи сортируются между каждым запросом.
Список параметров
У операторов HQL могут быть параметры. Вот пример:
| 
 1 
2 
 | 
def result = Person.executeQuery(    "from Person where firstName = ? and lastName = ?", ['John', 'Doe']) | 
Параметры могут быть переданы в виде списка. Первый параметр (John) используется в первом вопросительном знаке, второй параметр (Doe) используется во втором вопросительном знаке и так далее.
Результаты также могут быть разбиты на страницы
| 
 1 
2 
 | 
def result = Person.executeQuery(    "from Person where firstName = ? and lastName = ?", ['John', 'Doe'], [offset:0, max:5]) | 
Именованные параметры
Предоставление параметров списка обычно трудно читать и подвержено ошибкам. Проще использовать именованные параметры. Например:
| 
 1 
2 
3 
 | 
def result = Person.executeQuery(    "from Person where firstName = :searchFirstName and lastName = :searchLastName",     [searchFirstName:'John', searchLastName:'Doe']) | 
Двоеточие означает именованную переменную параметра. Затем значения могут быть переданы как карта значений.
Результаты также могут быть разбиты на страницы:
| 
 1 
2 
3 
 | 
def result = Person.executeQuery(    "from Person where firstName = :searchFirstName and lastName = :searchLastName",    [searchFirstName:'John', searchLastName:'Doe'], [offset:0, max:5]) | 
Вот более короткая версия:
| 
 1 
2 
3 
 | 
def result = Person.executeQuery(    "from Person where firstName = :searchFirstName and lastName = :searchLastName",    [searchFirstName:'John', searchLastName:'Doe'], [offset:0, max:5]) | 
Как выполнять СОЕДИНЕНИЯ
Вот пример классов домена отношения один ко многим:
| 
 1 
2 
3 
4 
5 
6 
7 
 | 
package asia.grails.testclass Purchase {    static hasMany = [items:PurchaseItem]    String customer    Date dateOfPurchase    double price} | 
| 
 1 
2 
3 
4 
5 
6 
7 
8 
 | 
package asia.grails.testclass PurchaseItem {    static belongsTo = Purchase    Purchase parentPurchase    String product    double price    int quantity} | 
Вот пример кода, который объединяет две таблицы:
| 
 1 
2 
 | 
def customerWhoBoughtPencils = Purchase.executeQuery(    "select p.customer from Purchase p join p.items i where i.product = 'Pencil' ") | 
Это возвращает всех клиентов, которые купили карандаши
executeUpdate
Мы можем обновить или удалить записи, используя executeUpdate. Это иногда более эффективно, особенно при работе с большими наборами записей.
Удалить
Вот несколько примеров того, как удалять записи, используя executeUpdated.
Удалить все записи человека в базе данных
| 
 1 
 | 
Purchase.executeUpdate("delete Person") | 
Вот разные способы удаления людей с именем Джон
| 
 1 
2 
3 
 | 
Person.executeUpdate("delete Person where firstName = 'John'")Person.executeUpdate("delete Person where firstName = ? ", ['John'])Person.executeUpdate("delete Person where firstName = :firstNameToDelete ", [firstNameToDelete:'John']) | 
Обновить
Вот несколько примеров того, как удалять записи, используя executeUpdated.
Вот разные способы, как заставить всех людей иметь возраст 15
| 
 1 
2 
3 
 | 
Person.executeUpdate("update Person set age = 15")Person.executeUpdate("update Person set age = ?", [15])Person.executeUpdate("update Person set age = :newAge", [newAge:15]) | 
Вот несколько способов установить возраст Джона Доу на 15 лет.
| 
 1 
2 
3 
4 
5 
6 
7 
 | 
Person.executeUpdate(    "update Person set age = 15 where firstName = 'John' and lastName = 'Doe'")Person.executeUpdate(    "update Person set age = ? where firstName = ? and lastName = ?", [15, 'John', 'Doe'])Person.executeUpdate(    "update Person set age = :newAge where firstName = :firstNameToSearch and lastName = :lastNameToSearch",    [newAge:15, firstNameToSearch:'John', lastNameToSearch:'Doe']) | 
| Ссылка: | Учебник Grails для начинающих — HQL Queries (executeQuery и executeUpdate) от нашего партнера по JCG Джонатана Тана в блоге Grails . |