Java 持久性查询语言 (JP-QL) 深受 HQL(Hibernate 本机查询语言)的启发。因此,两者都非常接近于 SQL,但却是可移植的且独立于数据库模式。熟悉 HQL 的人使用 JP-QL 应该不会有任何问题。事实上,HQL 是 JP-QL 的严格超集,并且你可以对两种类型的查询使用相同的查询 API。然而,可移植 JPA 应用程序应该坚持使用 JP-QL。
我们强烈建议你使用 Criteria 查询采用使类型安全的查询方法,请参阅 第九章,Criteria 查询。
select c from eg.Cat c
select c from Cat c
你可能已经注意到,你可以向类分配别名,as
关键字是可选的。别名允许你在查询的其他部分中引用 Cat
。
select cat from Cat as cat
select from, param from Formula as form, Parameter as param
使用小写字母作为查询别名的首字母被认为是良好的做法,符合 Java 本地变量的命名标准(例如 domesticCat
)。
你还可以使用 join
向关联实体或甚至是值集合的元素分配别名。
select cat, mate, kitten from Cat as cat inner join cat.mate as mate left outer join cat.kittens as kitten
select cat from Cat as cat left join cat.mate.kittens as kittens
inner join
,left outer join
构造可以简化。
select cat, mate, kitten from Cat as cat join cat.mate as mate left join cat.kittens as kitten
此外,“获取”连接允许关联或值集合随其父对象通过单一选择初始化。这在集合的情况下尤其有用。它有效地覆盖了关联和集合映射元数据中的获取选项。有关更多信息,请参阅 Hibernate 参考指南的性能章节。
select cat from Cat as cat inner join fetch cat.mate left join fetch cat.kittens
select cat from Cat as cat inner join fetch cat.mate left join fetch cat.kittens child left join fetch child.kittens
如果使用属性级延迟获取(带有字节码工具),可以使用 fetch all properties
强制 Hibernate 立即(在第一个查询中)获取延迟属性。这是 Hibernate 特有的选项
select doc from Document doc fetch all properties order by doc.name
select doc from Document doc fetch all properties where lower(doc.name) like '%cats%'
select
子句选择在查询结果集中返回的对象和属性。请考虑
select mate from Cat as cat inner join cat.mate as mate
查询将选择其他 Cat
的 mate
。实际上,您可以更简洁地表示此查询为
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
select new list(mother, offspr, mate.name) from DomesticCat as mother inner join mother.mate as mate left outer join mother.kittens as offspr
select new Family(mother, mate, offspr) from DomesticCat as mother join mother.mate as mate left join mother.kittens as offspr
select max(bodyWeight) as max, min(bodyWeight) as min, count(*) as n from Cat cat
当与 select new map
(HQL 特有功能)一起使用时,它最有用
select new map( max(bodyWeight) as max, min(bodyWeight) as min, count(*) as n ) from Cat cat
select avg(cat.weight), sum(cat.weight), max(cat.weight), count(cat) from Cat cat
您可以在 select 子句中使用算术运算符、连接和公认的 SQL 函数(取决于配置的方言,这是 HQL 特有的功能)
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
select cat from Cat as cat
from java.lang.Object o // HQL only
from Named n, Named m where n.name = m.name // HQL only
请注意,后两个查询需要多于一条 SQL SELECT
。这意味着 order by
子句并未按正确的顺序排列整个结果集。(这也意味着您无法使用 Query.scroll()
调用这些查询。)
使用 where
子句可以缩小返回的实例列表。如果不存在别名,可以通过名称来引用属性
select cat from Cat cat where cat.name='Fritz'
select foo from Foo foo, Bar bar where foo.startDate = bar.date
将返回 Foo
的所有实例,其中存在 Bar
的一个实例,其 date
属性等于 Foo
的 startDate
属性。复合路径表达式使 where
子句极其强大。请考虑
select cat from Cat cat where cat.mate.name is not null
此查询转换成一个含有表(内部)联接的 SQL 查询。如果您写类似这样的内容
select foo from Foo foo where foo.bar.baz.customer.address.city is not null
select cat, rival from Cat cat, Cat rival where cat.mate = rival.mate
select cat, mate from Cat cat, Cat mate where cat.mate = mate
特殊属性(小写)id
可用于引用对象的唯一标识符。(您还可以使用它映射的标识符属性名称)。请注意,此关键字是 HQL 特有的。
select cat from Cat as cat where cat.id = 123 select cat from Cat as cat where cat.mate.id = 69
也可以使用复合标识符的属性。假设 Person
有一个由 country
和 medicareNumber
组成的复合标识符。
select person from bank.Person person where person.id.country = 'AU' and person.id.medicareNumber = 123456
select account from bank.Account account where account.owner.id.country = 'AU' and account.owner.id.medicareNumber = 123456
同样,特殊属性 class
访问的是差异持久情况下的实例的差异值。嵌入在 where 子句中的 Java 类名将转换成它的差异值。同样,这也是 HQL 特有的。
select cat from Cat cat where cat.class = DomesticCat
您也可以指定组件或组合用户类型(以及组件的组件等)的属性。切勿尝试使用以组件类型的属性结尾的路径表达式(与组件的属性相对)。例如,如果 store.owner
是具有组件 address
的实体
store.owner.address.city // okay store.owner.address // error!
“任何”类型具有特殊属性 id
和 class
,允许我们以下述方式表示联接(其中 AuditLog.item
是使用 <any>
映射的属性)。Any
适用于 Hibernate
from AuditLog log, Payment payment where log.item.class = 'Payment' and log.item.id = payment.id
where
子句中允许的表达式包括您可以在 SQL 中编写的各种类型的事物
in
,not in
,between
,is null
,is not null
,is empty
,is not empty
,member of
和 not member of
“简单”情况,case ... when ... then ... else ... end
,和“搜索”情况,case when ... then ... else ... end
字符串连接 ...||...
或 concat(...,...)(对于可移植 JP-QL 查询,请使用 concat())
second(...)
,minute(...)
,hour(...)
,day(...)
,month(...)
,year(...)
(特定于 HQL)
任何函数或运算符:substring(), trim(), lower(), upper(), length(), locate(), abs(), sqrt(), bit_length()
TYPE ... in ...
,其中第一个参数是标识符变量,第二个参数是将多态性限制为的子类(或用括号括起来的子类列表)
日期的 JDBC 转义语法(取决于你的 JDBC 驱动器支持)(例如 where date = {d '2008-12-31'}
)
select cat from DomesticCat cat where cat.name between 'A' and 'B'
select cat from DomesticCat cat where cat.name in ( 'Foo', 'Bar', 'Baz' )
select cat from DomesticCat cat where cat.name not between 'A' and 'B'
select cat from DomesticCat cat where cat.name not in ( 'Foo', 'Bar', 'Baz' )
同样,is null
和 is not null
可用于测试空值。
布尔值可通过在 Hibernate 配置中声明 HQL 查询替换来在表达式中轻松使用
hibernate.query.substitutions true 1, false 0
这将从这个 HQL 翻译的 SQL 中用文字 1
和 0
替换关键词 true
和 false
select cat from Cat cat where cat.alive = true
你可以使用特殊属性 size
或特殊 size()
函数(HQL 特定功能)测试集合的大小。
select cat from Cat cat where cat.kittens.size > 0
select cat from Cat cat where size(cat.kittens) > 0
select cal from Calendar cal where maxelement(cal.holidays) > current date
select order from Order order where maxindex(order.items) > 100
select order from Order order where minelement(order.items) > 10000
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)
select cat from Cat cat where exists elements(cat.kittens)
select cat from Player p where 3 > all elements(p.scores)
select cat from Show show where 'fizard' in indices(show.acts)
JP-QL 允许你使用 KEY()
和 VALUE()
操作访问 map 的键或值(甚至可以使用 ENTRY()
访问 Entry 对象)。
SELECT i.name, VALUE(p) FROM Item i JOIN i.photos p WHERE KEY(p) LIKE ‘%egret’
在 HQL 中,可以通过索引引用编址集合(数组、列表、map)的元素(仅在 where 从句中)。
select order 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
select cat 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 )
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
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 having avg(kitten.weight) > 100 order by count(kitten) asc, sum(kitten.weight) desc
对于支持子查询的数据库,JP-QL 在查询中支持子查询。子查询必须被括号包围(通常由 SQL 聚合函数调用包围)。甚至允许使用相关子查询(引用外部查询中别名的子查询)。
select fatcat from Cat as fatcat where fatcat.weight > ( select avg(cat.weight) from DomesticCat cat )
select cat from DomesticCat as cat where cat.name = some ( select name.nickName from Name as name )
select cat from Cat as cat where not exists ( from Cat as mate where mate.mate = cat )
select cat from DomesticCat as cat where cat.name not in ( select name.nickName from Name as name )
对于 select 列表中有多个表达式的子查询,可以使用元组构造函数
select cat from Cat as cat where not ( cat.name, cat.color ) in ( select cat.name, cat.color from DomesticCat cat )
请注意,在某些数据库(但不是 Oracle 或 HSQLDB)中,可以在其他上下文中使用元组构造函数,例如查询组件或复合用户类型时
select cat from Person where name = ('Gavin', 'A', 'King')
select cat from Person where name.first = 'Gavin' and name.initial = 'A' and name.last = 'King')
有充分的理由让你不会想要做这种事情:首先,它并非完全可移植于不同的数据库平台;其次,该查询现在依赖于映射文档中属性的排序。
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
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
映射为列表而不是集合,则可以更轻松地编写查询。
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
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
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.name 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
版权所有 © 2005 Red Hat Inc. 和不同作者