Этот урок 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.test class 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.test class Purchase { static hasMany = [items:PurchaseItem] String customer Date dateOfPurchase double price } |
1
2
3
4
5
6
7
8
|
package asia.grails.test class 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 . |