Hibernate HQL 语法大全(下)

11.子查询

对于支持子查询的数据库,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 )

在select列表中包含一个表达式以上的子查询,你可以使用一个元组构造符(tuple constructors):

from Cat as cat where not ( cat.name, cat.color ) in ( select cat.name, cat.color from DomesticCat cat )

注意在某些数据库中(不包括Oracle与HSQL),你也可以在其他语境中使用元组构造符, 比如查询用户类型的组件与组合:

from Person where name = ('Gavin', 'A', 'King')

该查询等价于更复杂的:

from Person where name.first = 'Gavin' and name.initial = 'A' and name.last = 'King')

有两个很好的理由使你不应当作这样的事情:首先,它不完全适用于各个数据库平台;其次,查询现在依赖于映射文件中属性的顺序。

12.HQL示例

Hibernate查询可以非常的强大与复杂。实际上,Hibernate的一个主要卖点就是查询语句的威力。这里有一些例子,它们与我在最近的一个项目中使用的查询非常相似。注意你能用到的大多数查询比这些要简单的多!

下面的查询对于某个特定的客户的所有未支付的账单,在给定给最小总价值的情况下,返回订单的id,条目的数量和总价值,返回值按照总价值的结果进行排序。为了决定价格,查询使用了当前目录。作为转换结果的SQL查询,使用了ORDERORDER_LINEPRODUCTCATALOG 和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查询,该查询使用了表 PAYMENTPAYMENT_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()函数用以返回当前用户所属组织的组织帐号及组织未支付的账。它被转换成一个对表ACCOUNTPAYMENTPAYMENT_STATUSACCOUNT_TYPEORGANIZATION以及 ORG_USER进行的三个内连接,一个外连接和一个子选择的SQL查询。

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

13.批量的UPDATE & DELETE语句

HQL现在支持UPDATE与DELETE语句.

14.小技巧 & 小窍门

你可以统计查询结果的数目而不必实际的返回他们:

( (Integer) session.iterate("select count(*) from ....").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子句中为选择的大小(selection size)指定一个条件:

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

因为内连接(inner join)的原因,这个解决方案不能返回含有零个信息的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的属性可以被绑定到一个命名查询(named query)的参数上:

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与一个过滤器(filter)一起使用,集合(Collections)是可以分页的:

Query q = s.createFilter( collection, "" ); // 一个简单的过滤器 q.setMaxResults(PAGE_SIZE); q.setFirstResult(PAGE_SIZE * pageNumber); List page = q.list();

通过使用查询过滤器(query filter)可以将集合(Collection)的原素分组或排序:

Collection orderedCollection = s.filter( collection, "order by this.amount" ); Collection counts = s.filter( collection, "select this.type, count(this) group by this.type" );

不用通过初始化,你就可以知道一个集合(Collection)的大小:

( (Integer) session.iterate("select count(*) from ....").next() ).intValue();

posted on 2009-08-18 00:11  木鱼哥  阅读(185)  评论(0编辑  收藏  举报

导航