Этот урок 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 . |