Глава 16. HQL: Язык запросов Hibernate
Hibernate использует мощный язык запросов (The Hibernate Query Language (HQL)), который похож по внешнему виду на SQL. Однако, по сравнению с SQL, HQL полностью объектно-ориентирован и понимает такие понятия, как наследование, полиморфизм и ассоциация.
16.1. Чувствительность к регистру
За исключением имен классов и свойств Java, запросы не учитывают регистр. Таким образом, SeLeCT совпадает с SELEct таким же, как SELECT , но org.hibernate.eg.FOO не является org.hibernate.eg.Foo , а foo.barSet не является foo.BARSET .
В этом руководстве ключевые слова HQL используются в нижнем регистре. Некоторые пользователи находят запросы с ключевыми словами в верхнем регистре более читаемыми, но это соглашение непригодно для запросов, встроенных в код Java.
16.2. Секция «from»
Самый простой возможный запрос Hibernate имеет вид:
from eg.Cat
Этот запрос возвращает все экземпляры класса eg.Cat . Обычно вам не требуется квалифицировать имя класса, поскольку по умолчанию установлен auto-import . Например:
from Cat
Чтобы обратиться к Cat в других частях запроса, вам нужно будет присвоить псевдоним (alias). Например:
from Cat as cat
Этот запрос присваивает псевдоним cat экземплярам Cat , поэтому вы можете использовать этот псевдоним позже в запросе. Ключевое слово as необязательно. Вы также можете написать:
from Cat cat
Может встретиться несколько классов, что приведет к декартовому продукту или «перекрёстному (cross)» соединению (join).
from Formula, Parameter
from Formula as form, Parameter as param
Хорошей практикой является записывать псевдонимы, начиная с нижнего регистра, поскольку это согласуется с стандартами именования Java для локальных переменных (например, internalCat ).
16.3. Ассоциации и объединения (join)
Вы также можете назначать псевдонимы связанным сущностям или элементам коллекции значений с помощью join . Например:
from Cat as cat inner join cat.mate as mate left outer join cat.kittens as kitten
from Cat as cat left join cat.mate.kittens as kittens
from Formula form full join form.parameter param
Поддерживаемые типы соединений заимствованы из ANSI SQL:
- inner join
- left outer join
- right outer join
- full join (редко полезен)
inner join , left outer join и right outer join могут быть сокращены.
from Cat as cat join cat.mate as mate left join cat.kittens as kitten
Вы можете предоставить дополнительные условия соединения с использованием ключевого слова HQL with .
from Cat as cat left join cat.kittens as kitten with kitten.bodyWeight > 10.0
Соединение «fetch» позволяет инициализировать ассоциации или коллекции значений вместе с их родительскими объектами с использованием одного запроса. Это особенно полезно в случае коллекции. Он эффективно отменяет внешнее объединение и ленивые объявления файла отображения для ассоциаций и коллекций. Дополнительную информацию см. в разделе 20.1 «Стратегии выборки».
from Cat as cat inner join fetch cat.mate left join fetch cat.kittens
fetch join обычно не требует назначать псевдоним, потому что связанные объекты не должны и спользоваться в секции where (или в любой другой секции). Связанные объекты также не возвращаются непосредственно в результатах запроса. Вместо этого к ним можно получить доступ через родительский объект. Единственная причина, по которой вам может понадобиться псевдоним, заключается в том, что вы рекурсивно объединяете выборку (join fetching) дальнейшей коллекции:
from Cat as cat inner join fetch cat.mate left join fetch cat.kittens child left join fetch child.kittens
Конструкция fetch не может использоваться в запросах, вызванных с помощью функции iterate() (хотя можно использовать функцию scroll() ). Fetch не следует использовать вместе с setMaxResults() или setFirstResult() , так как эти операции основаны на строках результата, которые обычно содержат дубликаты в результирующей коллекции, следовательно, количество строк не является тем, что вы ожидаете. Fetch также не должно использоваться вместе с условием with . Возможно создание декартовой продукции, объединением выборки из нескольких коллекций в запросе, поэтому будьте осторожны в этом случае. Объединение выборки из нескольких ролей коллекции может привести к неожиданным результатам для отображённых bag, поэтому при формулировании запросов в этом случае рекомендуется ручное разграничение. Наконец, обратите внимание, что full join fetch и right join fetch не имеют смысла.
Если вы используете ленивую выборку уровня свойств (с инструментами байт-кода), можно заставить Hibernate немедленно получать ленивые свойства в первом запросе, используя fetch all properties .
from Document fetch all properties order by name
from Document doc fetch all properties where lower(doc.name) like '%cats%'
16.4. Формы синтаксиса join
HQL поддерживает две формы объединения: явные ( explicit ) и неявные ( implicit ).
Запросы, показанные в предыдущем разделе, используют явную форму, то есть где ключевое слово join явно используется в секции from. Это рекомендуемая форма.
Неявная форма не использует ключевое слово join. Вместо этого ассоциации «разыменовываются» с использованием точечной нотации. неявные объединения могут появляться в любом из секций HQL. Неявный результат объединения во внутренних объединениях в результирующей SQL-инструкции.
from Cat as cat where cat.mate.name like '%s%'
16.5. Ссылаясь на свойство идентификатора
Существует два способа ссылки на свойство идентификатора сущности:
- Специальное свойство (нижний регистр) id может использоваться для ссылки на свойство идентификатора сущности, при условии, что сущность не определяет неидентифицирующее свойство с именем id.
- Если сущность определяет именованное свойство идентификатора, вы можете использовать это имя свойства.
Ссылки на составные свойства идентификатора следуют тем же правилам именования. Если у объекта есть неидентифицирующее свойство с именем id, свойство составного идентификатора может ссылаться только на его определённое имя. В противном случае специальное свойство id может использоваться для ссылки на свойство идентификатора.
Важно
Обратите внимание, что начиная с версии 3.2.2 это поведение значительно изменилось. В предыдущих версиях id всегда ссылался на свойство идентификатора независимо от его фактического имени. Разветвление этого решения заключалось в том, что неидентифицирующие свойства с именем id никогда не смогут получить ссылку в запросах Hibernate.
16.6. Секция «select»
Секция select выбирает, какие объекты и свойства возвращать в наборе результатов запроса. Рассмотрим следующее:
select mate from Cat as cat inner join cat.mate as mate
В запросе будут выбраны все mate у Cat . Вы можете выразить этот запрос более компактно так:
select cat.mate from Cat cat
Запросы могут возвращать свойства любого типа значений, включая свойства типа компонента:
select cat.name from DomesticCat cat where cat.name like 'fri%'
select cust.name.firstName from Customer as cust
Запросы могут возвращать несколько объектов и/или свойств в виде массива типа Object[] :
select mother, offspr, mate.name from DomesticCat as mother inner join mother.mate as mate left outer join mother.kittens as offspr
Или в виде списка List :
select new list(mother, offspr, mate.name) from DomesticCat as mother inner join mother.mate as mate left outer join mother.kittens as offspr
Или — если предположить, что класс Family имеет соответствующий конструктор — как фактический типобезопасный Java-объекта:
select new Family(mother, mate, offspr) from DomesticCat as mother join mother.mate as mate left join mother.kittens as offspr
Вы можете назначить псевдонимы для выбранных выражений, используя as :
select max(bodyWeight) as max, min(bodyWeight) as min, count(*) as n from Cat cat
Это наиболее полезно при использовании вместе с select new map :
select new map( max(bodyWeight) as max, min(bodyWeight) as min, count(*) as n ) from Cat cat
Этот запрос возвращает Map из псевдонимов выбранных значений.
16.7. Агригатные функции
Запросы HQL могут даже возвращать результаты агрегатных функций по свойствам:
select avg(cat.weight), sum(cat.weight), max(cat.weight), count(cat) from Cat cat
Поддерживаемые агрегированные функции:
- avg(. ), sum(. ), min(. ), max(. )
- count(*)
- count(. ), count(distinct . ), count(all. )
Вы можете использовать арифметические операторы, конкатенацию и известные функции SQL в секции select:
select cat.weight + sum(kitten.weight) from Cat cat join cat.kittens kitten group by cat.id, cat.weight
select firstName||' '||initial||' '||upper(lastName) from Person
Ключевые слова distinct и all могут использоваться и имеют ту же семантику, что и в SQL.
select distinct cat.name from Cat cat
select count(distinct cat.name), count(cat) from Cat cat
16.8. Полиморфные запросы
from Cat as cat
возвращают экземпляры не только Cat , но и подклассов, таких как DomesticCat . Запросы Hibernate могут называть любой класс или интерфейс Java в секции from . Запрос вернет экземпляры всех постоянных классов, которые расширяют этот класс или реализуют интерфейс. Следующий запрос будет возвращать все постоянные объекты:
from java.lang.Object o
Интерфейс Named может быть реализован различными постоянными классами:
from Named n, Named m where n.name = m.name
Этим двум запросам потребуются более одного SQL SELECT . Это означает, что секция order by неверно упорядочивает весь набор результатов. Это также означает, что вы не можете вызывать эти запросы с помощью Query.scroll() .
16.9. Секция «where»
Предложение where позволяет уточнить список возвращаемых экземпляров. Если псевдоним не существует, вы можете ссылаться на свойства по имени:
from Cat where name='Fritz'
Если есть псевдоним, используйте квалифицированное имя свойства:
from Cat as cat where cat.name='Fritz'
Этот запрос возвращает экземпляры Cat с именем «Fritz».
select foo from Foo foo, Bar bar where foo.startDate = bar.date
возвращает все экземпляры Foo с экземпляром bar с свойством date , равным свойству startDate для Foo . Сложные выражения делают секцию where чрезвычайно мощной. Рассмотрим следующее:
from Cat cat where cat.mate.name is not null
Этот запрос переводится в SQL-запрос с табличным (внутренним) соединением. Например:
from Foo foo where foo.bar.baz.customer.address.city is not null
приведет к запросу, который потребует объединения четырёх таблиц в SQL.
Оператор = можно использовать для сравнения не только свойств, но и экземпляров:
from Cat cat, Cat rival where cat.mate = rival.mate
select cat, mate from Cat cat, Cat mate where cat.mate = mate
Специальное свойство (нижний регистр) id может использоваться для ссылки на уникальный идентификатор объекта. Дополнительную информацию см. в разделе 16.5. «Ссылаясь на свойство идентификатора».
from Cat as cat where cat.id = 123
from Cat as cat where cat.mate.id = 69
Второй запрос эффективен и не требует соединения таблиц.
Также можно использовать свойства составных идентификаторов. Рассмотрим следующий пример, в котором у Person есть составные идентификаторы, состоящие из country и medicareNumber :
from bank.Person person where person.id.country = 'AU' and person.id.medicareNumber = 123456
from bank.Account account where account.owner.id.country = 'AU' and account.owner.id.medicareNumber = 123456
Еще раз, второй запрос не требует соединения таблиц.
См. раздел 16.5. «Ссылаясь на свойство идентификатора» для получения дополнительной информации о ссылках на свойства идентификатора).
Специальный свойство class обращается к значению дискриминатора экземпляра в случае полиморфного постоянства (polymorphic persistence). Имя класса Java, встроенное в секцию where, будет переведено на его значение дискриминатора.
from Cat cat where cat.class = DomesticCat
Вы также можете использовать компоненты, или составные пользователбские типы, или свойства указанных типов компонентов. Дополнительную информацию см. в разделе 16.17. «Компоненты».
«Любой» тип имеет специальные свойства id и class , которые позволяют вам выразить объединение следующим образом (где AuditLog.item — это свойство, отображаемое в ):
from AuditLog log, Payment payment where log.item.class = 'Payment' and log.item.id = payment.id
Класс log.item.class и payment.class будут ссылаться на значения полностью разных столбцов базы данных в вышеуказанном запросе.
16.10. Выражения
Выражения, используемые в предложении where , включают следующее:
- математические операторы: +, -, *, /
- двоичные операторы сравнения: =, >=, , !=, like
- логические операции: and, or, not
- скобки ( ) , которые указывают на группировку
- in, not in, between, is null, is not null, is empty, is not empty, member of и not member of
- «Простые» case: case . when . then . else . end и «поисковые» case: case when . then . else . end
- сравнение строк . ||. или concat(. )
- current_date(), current_time() и current_timestamp()
- second(. ), minute(. ), hour(. ), day(. ), month(. ), and year(. )
- Любая функция или оператор, определенные EJB-QL 3.0: substring(), trim(), lower(), upper(), length(), locate(), abs(), sqrt(), bit_length(), mod()
- coalesce() и nullif()
- str() для преобразования числовых или временных значений в читаемую строку
- cast (. as . ) , где второй аргумент — это имя типа Hibernate и extract (. from . ) , если ANSI cast() и extract() поддерживается базой данных.
- функция HQL index() , которая применяется к псевдонимам объединённой индексированной коллекции.
- HQL-функции, которые принимают выражения, связанные с коллекцией: size(), mylement(), maxelement(), minindex(), maxindex() , а также специальные э elements() и функции indices , которые могут быть количественно определены с использованием some, all, exists, any, in .
- Любая скалярная функция SQL с поддержкой базы данных типа: sign(), trunc(), rtrim(), and sin()
- Позиционные параметры JDBC-стиля ?
- именованные параметры: :name, :start_date и :x1
- SQL литералы: ‘foo’, 69, 6.66E+2, ‘1970-01-01 10:00:01.0’
- Java public static final константы eg.Color.TABBY
in и between может использоваться следующим образом:
from DomesticCat cat where cat.name between 'A' and 'B'
from DomesticCat cat where cat.name in ( 'Foo', 'Bar', 'Baz' )
Отрицательные формы можно записать следующим образом:
from DomesticCat cat where cat.name not between 'A' and 'B'
from DomesticCat cat where cat.name not in ( 'Foo', 'Bar', 'Baz' )
Аналогичным образом, is null и is not null могут быть использованы для проверки нулевых значений.
Булевы могут быть легко использованы в выражениях, объявлением в конфигурации Hibernate для подстановки HQL-запросов:
true 1, false 0
Это заменит ключевые слова true и false литералами 1 и 0 при трансляции из HQL в SQL:
from Cat cat where cat.alive = true
Вы можете проверить размер коллекции специальным свойством size или специальной функцией size() .
from Cat cat where cat.kittens.size > 0
from Cat cat where size(cat.kittens) > 0
Для индексированных коллекций вы можете ссылаться на минимальный и максимальный индексы, используя функции minindex и maxindex . Аналогично, вы можете ссылаться на минимальный и максимальный элементы коллекции базового типа, используя функции minelement и maxelement . Например:
from Calendar cal where maxelement(cal.holidays) > current_date
from Order order where maxindex(order.items) > 100
from Order order where minelement(order.items) > 10000
Функции SQL any, some, all, exist, in поддерживаются при передаче элемента или набора индексов коллекции (функции elements и indices ) или результата подзапроса (см. ниже):
select mother from Cat as mother, Cat as kit where kit in elements(foo.kittens)
select p from NameList list, Person p where p.name = some elements(list.names)
from Cat cat where exists elements(cat.kittens)
from Player p where 3 > all elements(p.scores)
from Show show where 'fizard' in indices(show.acts)
Обратите внимание, что эти конструкции — size, elements, indices, minindex, maxindex, minelement, maxelement — могут использоваться только в секции where в Hibernate.
Элементы индексированных коллекций (массивы, списки и карты) могут упоминаться только индексом в секции where:
from Order order where order.items[0].id = 1234
select person from Person person, Calendar calendar where calendar.holidays['national day'] = person.birthDay and person.nationality.calendar = calendar
select item from Item item, Order order where order.items[ order.deliveredItemIndices[0] ] = item and order.id = 11
select item from Item item, Order order where order.items[ maxindex(order.items) ] = item and order.id = 11
Выражение внутри [] может быть даже арифметическим выражением:
select item from Item item, Order order where order.items[ size(order.items) - 1 ] = item
HQL также предоставляет встроенную функцию index() для элементов ассоциации «один-ко-многим» или коллекции значений.
select item, index(item) from Order order join order.items item where index(item) < 5
Скалярные функции SQL, поддерживаемые базой данных, могут быть использованы:
from DomesticCat cat where upper(cat.name) like 'FRI%'
Подумайте, насколько длиннее и менее читаемым мог бы быть следующий запрос на SQL:
select cust from Product prod, Store store inner join store.customers cust where prod.name = 'widget' and store.location.name in ( 'Melbourne', 'Sydney' ) and prod = all elements(cust.currentOrder.lineItems)
Подсказка: что-то вроде
SELECT cust.name, cust.address, cust.phone, cust.id, cust.current_order FROM customers cust, stores store, locations loc, store_customers sc, product prod WHERE prod.name = 'widget' AND store.loc_id = loc.id AND loc.name IN ( 'Melbourne', 'Sydney' ) AND sc.store_id = store.id AND sc.cust_id = cust.id AND prod.id = ALL( SELECT item.prod_id FROM line_items item, orders o WHERE item.order_id = o.id AND cust.current_order = o.id )
16.11. Секция «order by»
Список, возвращаемый запросом, может быть упорядочен любым свойством возвращаемого класса или компонента:
from DomesticCat cat order by cat.name asc, cat.weight desc nulls first, cat.birthdate
Необязательный asc или desc указывают соответственно на восходящий или нисходящий порядок.
Необязательные nulls first или nulls last указывают приоритет значений null при сортировке.
16.12. Секция «group by»
Запрос, возвращающий агрегированные значения, может быть сгруппирован любым свойством возвращаемого класса или компонента:
select cat.color, sum(cat.weight), count(cat) from Cat cat group by cat.color
select foo.id, avg(name), max(name) from Foo foo join foo.names name group by foo.id
Секция having также разрешена.
select cat.color, sum(cat.weight), count(cat) from Cat cat group by cat.color having cat.color in (eg.Color.TABBY, eg.Color.BLACK)
Функции SQL и агрегированные функции разрешены в секциях having и order by , если они поддерживаются базой данных (например в MySQL их нет).
select cat from Cat cat join cat.kittens kitten group by cat.id, cat.name, cat.other, cat.properties having avg(kitten.weight) > 100 order by count(kitten) asc, sum(kitten.weight) desc
Ни секция group by , ни секция order by не могут содержать арифметические выражения. Также, в настоящее время, Hibernate не расширяет сгруппированную сущность, поэтому вы не можете писать group by cat , если все свойства cat не агрегированы. Вы должны явно указать все неагрегированные свойства.
16.13. Подзапросы
Для баз данных, которые поддерживают подзапросы, Hibernate поддерживает подзапросы внутри запросах. Подзапрос должен быть окружен скобками (часто с помощью вызова агреганой функции SQL). Разрешены даже коррелированные подзапросы (подзапросы, которые ссылаются на псевдонимы во внешнем запросе).
from Cat as fatcat where fatcat.weight > ( select avg(cat.weight) from DomesticCat cat )
from DomesticCat as cat where cat.name = some ( select name.nickName from Name as name )
from Cat as cat where not exists ( from Cat as mate where mate.mate = cat )
from DomesticCat as cat where cat.name not in ( select name.nickName from Name as name )
Обратите внимание, что подзапросы HQL могут встречаться только в секциях select или where.
Обратите внимание, что в подзапросах также может использоваться синтаксис row value constructor . Дополнительную информацию см. в разделе 16.18. «Синтаксис конструктора значения записи».
16.14. Примеры HQL
Запросы Hibernate могут быть довольно мощными и сложными. Фактически, сила языка запросов является одной из основных сильных сторон Hibernate. Следующие примеры запросов аналогичны запросам, которые были использованы в последних проектах. Обратите внимание, что большинство запросов, которые вы напишете, будут намного проще, чем следующие примеры.
Следующий запрос возвращает идентификатор заказа, количество элементов, заданное минимальное общее значение и общее значение заказа для всех неоплаченных заказов для конкретного клиента. Результаты упорядочиваются по общему значению. При определении цен он использует текущий каталог. Результирующий SQL-запрос по отношению к таблицам ORDER, ORDER_LINE, PRODUCT, CATALOG и PRICE имеет четыре внутренних соединения и (некоррелированный) подзапрос.
select order.id, sum(price.amount), count(item) from Order as order join order.lineItems as item join item.product as product, Catalog as catalog join catalog.prices as price where order.paid = false and order.customer = :customer and price.product = product and catalog.effectiveDate < sysdate and catalog.effectiveDate >= all ( select cat.effectiveDate from Catalog as cat where cat.effectiveDate < sysdate ) group by order having sum(price.amount) >:minAmount order by sum(price.amount) desc
Какое чудовище! На самом деле, в реальной жизни я не очень увлекаюсь подзапросами, поэтому мой запрос, в действительности, был бы более похожим на это:
select order.id, sum(price.amount), count(item) from Order as order join order.lineItems as item join item.product as product, Catalog as catalog join catalog.prices as price where order.paid = false and order.customer = :customer and price.product = product and catalog = :currentCatalog group by order having sum(price.amount) > :minAmount order by sum(price.amount) desc
Следующий запрос подсчитывает количество платежей в каждом статусе, за исключением всех платежей в статусе AWAITING_APPROVAL , где текущий пользовательский статус изменил последнее значение статуса. Он преобразуется в SQL-запрос с двумя внутренними соединениями и коррелированным подзапросом в отношении таблиц PAYMENT, PAYMENT_STATUS и PAYMENT_STATUS_CHANGE .
select count(payment), status.name from Payment as payment join payment.currentStatus as status join payment.statusChanges as statusChange where payment.status.name <> PaymentStatus.AWAITING_APPROVAL or ( statusChange.timeStamp = ( select max(change.timeStamp) from PaymentStatusChange change where change.payment = payment ) and statusChange.user <> :currentUser ) group by status.name, status.sortOrder order by status.sortOrder
Если коллекция statusChanges была отображена как list, а не set, запрос был бы намного проще писать.
select count(payment), status.name from Payment as payment join payment.currentStatus as status where payment.status.name <> PaymentStatus.AWAITING_APPROVAL or payment.statusChanges[ maxIndex(payment.statusChanges) ].user <> :currentUser group by status.name, status.sortOrder order by status.sortOrder
Следующий запрос использует функцию MS SQL Server isNull() для возврата всех счетов и невыплаченных платежей для организации, к которой принадлежит текущий пользователь. Он преобразуется в SQL-запрос с тремя внутренними соединениями, внешним соединением и подзапросом в таблицах ACCOUNT, PAYMENT, PAYMENT_STATUS, ACCOUNT_TYPE, ORGANIZATION и ORG_USER .
select account, payment from Account as account left outer join account.payments as payment where :currentUser in elements(account.holder.users) and PaymentStatus.UNPAID = isNull(payment.currentStatus.name, PaymentStatus.UNPAID) order by account.type.sortOrder, account.accountNumber, payment.dueDate
Для некоторых баз данных нам нужно будет покончить с подзапросом (коррелированным).
select account, payment from Account as account join account.holder.users as user left outer join account.payments as payment where :currentUser = user and PaymentStatus.UNPAID = isNull(payment.currentStatus.name, PaymentStatus.UNPAID) order by account.type.sortOrder, account.accountNumber, payment.dueDate
16.15. Массовое обновление и удаление
HQL теперь поддерживает инструкции update, delete и insert . select . . Дополнительную информацию см. в разделе Операции в DML-стиле.
16.16. Советы и хитрости
Вы можете подсчитать количество результатов запроса, не возвращая их:
( (Integer) session.createQuery("select count(*) from . ").iterate().next() ).intValue()
Чтобы упорядочить результат по размеру коллекции, используйте следующий запрос:
select usr.id, usr.name from User as usr left join usr.messages as msg group by usr.id, usr.name order by count(msg)
Если ваша база данных поддерживает подзапросы, вы можете поместить условие на размер выбора в секцию where вашего запроса:
from User usr where size(usr.messages) >= 1
Если ваша база данных не поддерживает подзапросы, используйте следующий запрос:
select usr.id, usr.name from User usr join usr.messages msg group by usr.id, usr.name having count(msg) >= 1
Поскольку это решение не может вернуть User с нулём сообщений из-за внутреннего соединения, следующая форма также полезна:
select usr.id, usr.name from User as usr left join usr.messages as msg group by usr.id, usr.name having count(msg) = 0
Свойства JavaBean могут быть привязаны к именованным параметрам запроса:
Query q = s.createQuery("from foo Foo as foo where foo.name=:name and foo.size=:size"); q.setProperties(fooBean); // fooBean имеет getName() и getSize() List foos = q.list();
Коллекции можно просмотреть с помощью интерфейса Query с фильтром:
Query q = s.createFilter( collection, "" ); // тривиальный фильтр q.setMaxResults(PAGE_SIZE); q.setFirstResult(PAGE_SIZE * pageNumber); List page = q.list();
Элементы коллекции можно упорядочить или сгруппировать с помощью фильтра запросов:
Collection orderedCollection = s.filter( collection, "order by this.amount" ); Collection counts = s.filter( collection, "select this.type, count(this) group by this.type" );
Вы можете найти размер коллекции без ее инициализации:
( (Integer) session.createQuery("select count(*) from . ").iterate().next() ).intValue();
16.17. Компоненты
Компоненты могут использоваться аналогично простым типам значений, которые используются в запросах HQL. Они могут отображаться в секции следующим образом:
select p.name from Person p
select p.name.first from Person p
где свойство имени Person является компонентом. Компоненты также могут использоваться в секции where:
from Person p where p.name = :name
from Person p where p.name.first = :firstName
Компоненты также могут использоваться в секции order by :
from Person p order by p.name
from Person p order by p.name.first
Другое распространенное использование компонентов приведено в разделе 16.18 «Синтаксис конструктора значения записи».
16.18. Синтаксис конструктора значения записи
HQL поддерживает использование синтаксиса конструктора значений записи (row value constructor) ANSI SQL, иногда называемого синтаксисом AS tuple , хотя база данных может не поддерживать это понятие. Здесь мы обычно ссылаемся на многозначные сравнения, обычно связанные с компонентами. Рассмотрим сущность Person , которая определяет компонент имени:
from Person p where p.name.first='John' and p.name.last='Jingleheimer-Schmidt'
Это допустимый синтаксис, хотя он немного избыточный. Вы можете сделать его более кратким, используя синтаксис конструктора значения записи:
from Person p where p.name=('John', 'Jingleheimer-Schmidt')
Также может быть полезно указать это в секции select :
select p.name from Person p
Использование синтаксиса конструктора значений записи также может быть полезным при использовании подзапросов, которые необходимо сравнивать с несколькими значениями:
from Cat as cat where not ( cat.name, cat.color ) in ( select cat.name, cat.color from DomesticCat cat )
Одна вещь, которую следует учитывать при принятии решения о том, хотите ли вы использовать этот синтаксис, заключается в том, что запрос будет зависеть от порядка под-свойств компонента в метаданных.
Язык запросов 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();
Руководство по Hibernate. Язык запросов Hibernate (HQL).
HQL (Hibernate Query Language) – это объекто-ориентированный (далее – ОО) язык запросов, который крайне похож на SQL.
Отличие между HQL и SQL состоит в том, что SQL работает таблицами в базе данных (далее – БД) и их столбацами, а HQL – с сохраняемыми объектами (Persistent Objects) и их полями (аттрибутами класса).
Hibernate трнаслирует HQL – запросы в понятные для БД SQL – запросы, которые и выполняют необходимые нам действия в БД.
Мы также имеем возможность испольщовать обычные SQL – запросы в Hibernate используя Native SQL, но использование HQL является более предпочтительным.
Давайте рассмотрим основные ключевые слова языка HQL:
FROM
Если мы хотим загрузить в память наши сохраняемые объекты, то мы будем использовать ключевое слово FROM. Вот пример его использования:
Query query = session.createQuery("FROM Developer"); List developers = query.list();
Developer – это POJO – класс Developer.java, который ассоцииргван с таблицей в шагей БД.
INSERT
Мы используем ключевое слово INSERT, в том случае, если хотим добавить запись в таблицу нашей БД.
Вот пример использования этого ключевого слова:
Query query = session.createQuery("INSERT INTO Developer (firstName, lastName, specialty, experience)");
UPDATE
Ключевое слово UPDATE используется для обновления одного или нескольких полей объекта. Вот так это выглядит на практике:
Query query = session.createQuery(UPDATE Developer SET experience =: experience WHERE developerId); query.setParameter("expericence", 3);
DELETE
Это ключевоеслово используется для удаления одного или нескольких объектов. Пример использования:
Query query = session.createQuery("DELETE FROM Developer WHERE query.setParameter("developerId", 1);
SELECT
Если мы хотим получить запись из таблицы нашей БД, то мы должны использоваьб ключевое слово SELECT. Пример использования:
Query query = session.createQuery("SELECT D.lastName FROM Developer D"); List developers = query.list();
AS
В предыдущем примере мы использовали запись формы Developer D. С использованием опционального ключевого слова AS это будет выглядеть так:
Query query = session.createQuery("FROM Developer AS D"); List developers = query.list();
WHERE
В том случае, если мы хотим получить объекты, которые соответствуют опредлённым параметрам, то мы должны использовать ключевое слово WHERE. На практике это выглядит следующим образом:
Query query = session.createQuery("FROM Developer D WHERE D.id = 1"); List developer = query.list();
ORDER BY
Жля того, чтобы отсортировать список обхектов, полученных в результате запроса, мы должны применить ключевое слово ORDER BY. Нам необходимо укаать параметр, по которому список будет отсортирован и тип сортировки – по возрастанию (ASC) или по убыванию (DESC). В виде кода это выгллядит так:
Query query = session.createQuery("FROM Developer D WHERE experience > 3 ORDER BY D.experience DESC");
GROUP BY
С помощью ключевого слова GROUP BY мы можем группировать данные, полученные из БД по какому-либо признаку. Вот простой пример применения данного ключевого слова:
Query query = session.createQuery("SELECT MAX(D.experience), D.lastName, D.specialty FROM Developer D GROUP BY D.lastName"); List developers = query.list();
Методы аггрегации
Язык запросов Hibernate (HQL) пожжерживает различные методы аггрегации, которые доступны и в SQL. HQL поддреживает слудующие методы:
min(имя свойства)
Мнимальное значение данного свойства.
max(имя свойства)
Максимальное занчение данного свойтсва.
sum(имя свойства)
Сумма всех занчений данного свойтсва.
avg(имя свойства)
Среднее арифметическое всех значений данного свойства
count(имя свойства)
Какое количество раз данное свойство встречается в результате.
В данной статье мы ознакомились с основами языка запросов Hibernate (HQL).
Язык запросов Hibernate (HQL)
HQL (Hibernate Query Language) – это объектно-ориентированный (далее – ОО) язык запросов, который крайне похож на SQL.
Отличие между HQL и SQL состоит в том, что SQL работает таблицами в базе данных (далее – БД) и их столбцами, а HQL – с сохраняемыми объектами (Persistent Objects) и их полями (атрибутами класса).
Hibernate транслирует HQL- запросы в понятные для БД SQL - запросы, которые и выполняют необходимые нам действия в БД.
Мы также имеем возможность использовать обычные SQL -запросы в Hibernate, используя Native SQL, но использование HQL является более предпочтительным.
Давайте рассмотрим основные ключевые слова языка HQL:
FROM
Если мы хотим загрузить в память наши сохраняемые объекты, то будем использовать ключевое слово FROM. Вот пример его использования:
Query query = session.createQuery("FROM Developer"); List developers = query.list();
Developer – это POJO-класс Developer.java, который ассоциирован с таблицей в шаге БД.
INSERT
Мы используем ключевое слово INSERT в том случае, если хотим добавить запись в таблицу нашей БД.
Вот пример использования этого ключевого слова:
Query query = session.createQuery("INSERT INTO Developer (firstName, lastName, specialty, experience)");
UPDATE
Ключевое слово UPDATE используется для обновления одного или нескольких полей объекта. Вот так это выглядит на практике:
Query query = session.createQuery(UPDATE Developer SET experience =: experience WHERE developerId); query.setParameter("expericence", 3);
DELETE
Это ключевое слово используется для удаления одного или нескольких объектов. Пример использования:
Query query = session.createQuery("DELETE FROM Developer WHERE query.setParameter("developerId", 1);
SELECT
Если мы хотим получить запись из таблицы нашей БД, то мы должны использовать ключевое слово SELECT. Пример использования:
Query query = session.createQuery("SELECT D.lastName FROM Developer D"); List developers = query.list();
AS
В предыдущем примере мы использовали запись формы Developer D. С использованием опционального ключевого слова AS это будет выглядеть так:
Query query = session.createQuery("FROM Developer AS D"); List developers = query.list();
WHERE
В том случае, если мы хотим получить объекты, которые соответствуют опредленным параметрам, то мы должны использовать ключевое слово WHERE. На практике это выглядит следующим образом:
Query query = session.createQuery("FROM Developer D WHERE D.id = 1"); List developer = query.list();
ORDER BY
Жля того, чтобы отсортировать список объектов, полученных в результате запроса, мы должны применить ключевое слово ORDER BY. Нам необходимо указать параметр, по которому список будет отсортирован, и тип сортировки – по возрастанию (ASC) или по убыванию (DESC). В виде кода это выглядит так:
Query query = session.createQuery("FROM Developer D WHERE experience > 3 ORDER BY D.experience DESC");
GROUP BY
С помощью ключевого слова GROUP BY мы можем группировать данные, полученные из БД по какому-либо признаку. Вот простой пример применения данного ключевого слова:
Query query = session.createQuery("SELECT MAX(D.experience), D.lastName, D.specialty FROM Developer D GROUP BY D.lastName"); List developers = query.list();
Методы аггрегации
Язык запросов Hibernate (HQL) поддерживает различные методы агрегации, которые доступны и в SQL. HQL поддерживает следующие методы:
min(имя свойства)
Минимальное значение данного свойства.
max(имя свойства)
Максимальное значение данного свойства.
sum(имя свойства)
Сумма всех значений данного свойства.
avg(имя свойства)
Среднее арифметическое всех значений данного свойства
count(имя свойства)
Какое количество раз данное свойство встречается в результате.
