11.12. HQL示例
Hibernate查詢可以非常強大復雜。實際上,強有力的查詢語言是Hibernate的主要賣點之一。下面給出的示例與我在近期實際
下面的查詢對特定的客戶,根據(jù)給定的最小總計值(minAmount),查詢出所有未付訂單,返回其訂單號、貨品總數(shù)、訂單總金額,結果按照總金額排序。在決定價格的時候,參考當前目錄。產(chǎn)生的SQL查詢,在ORDER,ORDER_LINE,PRODUCT,CATALOG和PRICE表之間有四個內部連接和一個沒有產(chǎn)生關聯(lián)的字查詢。
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
好家伙,真長!實際上,在現(xiàn)實生活中我并不是非常熱衷于子查詢,所以我的查詢往往是這樣的:
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
下面的查詢統(tǒng)計
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
假若我已經(jīng)把statusChange集合映射為一個列表而不是一個集合的話,查詢寫起來會簡單很多。
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()函數(shù),返回當前用戶所屬的組織所有賬戶和未付支出。翻譯為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
Top
第 11 章 Hibernate查詢語言(Query Language), 即HQL
上一頁 下一頁
--------------------------------------------------------------------------------
第 11 章 Hibernate查詢語言(Query Language), 即HQL
Hibernate裝備了一種極為有力的查詢語言,(有意地)看上去很像SQL。但是別被
11.1. 大小寫敏感性(Case Sensitivity)
除了Java類和屬性名稱外,查詢都是大小寫不敏感的。 所以, SeLeCT 和 sELEct 以及 SELECT 相同的,但是 net.sf.hibernate.eg.FOO 和 net.sf.hibernate.eg.Foo 是不同的, foo.barSet 和 foo.BARSET也是不同的。
本手冊使用小寫的HQL關鍵詞。有些用戶認為在查詢中使用大寫的關鍵字更加易讀,但是我們認為嵌入在
11.2. from 子句
可能最簡單的Hibernate查詢是這樣的形式:
from eg.Cat
它簡單的返回所有eg.Cat類的實例。
大部分情況下,你需要賦予它一個別名(alias),因為你在查詢的其他地方也會引用這個Cat。
from eg.Cat as cat
上面的語句為Cat賦予了一個別名cat 。所以后面的查詢可以用這個簡單的別名了。as關鍵字是可以省略的,我們也可以寫成這樣:
from eg.Cat cat
可以出現(xiàn)多個類,結果是它們的笛卡爾積,或者稱為“交叉”連接。
from Formula, Parameter
from Formula as form, Parameter as param
讓查詢中的別名服從首字母小寫的規(guī)則,我們認為這是一個好習慣。這和Java對局部變量的命名規(guī)范是一致的。(比如,domesticCat).
11.3. 聯(lián)合(Associations)和連接(joins)
你可以使用join定義兩個實體的連接,同時指明別名。
from eg.Cat as cat
inner join cat.mate as mate
left outer join cat.kittens as kitten
from eg.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 eg.Cat as cat
join cat.mate as mate
left join cat.kittens as kitten
并且,加上 "fetch"后綴的抓取連接可以讓聯(lián)合的對象隨著它們的父對象的初始化而初始化,只需要一個select語句。這在初始化一個集合的時候特別有用。它有效地覆蓋了映射文件中對關聯(lián)和集合的外連接定義。
from eg.Cat as cat
inner join fetch cat.mate
left join fetch cat.kittens
抓取連接一般不需要賦予別名,因為被聯(lián)合的對象應該不會在where子句(或者任何其它子句)中出現(xiàn)。并且,被聯(lián)合的對象也不會在查詢結果中直接出現(xiàn)。它們是通過父對象進行訪問的。
請注意,目前的實現(xiàn)中,在一次查詢中只會抓取一個集合(其他的一切都做不到。)(?原文為:only one collection role may be fetched in a query)。也請注意,在使用scroll()或者 iterate()方式調用的查詢中,是禁止使用fetch構造的。最后,請注意full join fetch和right join fetch是沒有意義的。
11.4. select子句
select子句選擇在結果集中返回哪些對象和屬性。思考一下下面的例子:
select mate
from eg.Cat as cat
inner join cat.mate as mate
這個查詢會選擇出作為其它貓(Cat)朋友(mate)的那些貓。當然,你可以更加直接的寫成下面的形式:
select cat.mate from eg.Cat cat
你甚至可以選擇集合元素,使用特殊的elements
select elements(cat.kittens) from eg.Cat cat
查詢可以返回任何值類型的屬性,包括組件類型的屬性:
select cat.name from eg.DomesticCat cat
where cat.name like ‘fri%‘
select cust.name.firstName from Customer as cust
查詢可以用元素類型是Object[]的一個數(shù)組返回多個對象和/或多個屬性。
select mother, offspr, mate.name
from eg.DomesticCat as mother
inner join mother.mate as mate
left outer join mother.kittens as offspr
或者實際上是類型安全的Java對象
select new Family(mother, mate, offspr)
from eg.DomesticCat as mother
join mother.mate as mate
left join mother.kittens as offspr
上面的代碼假定Family有一個合適的構造函數(shù)。
11.5. 統(tǒng)計函數(shù)(Aggregate functions)
HQL查詢可以返回屬性的統(tǒng)計函數(shù)的結果。
select avg(cat.weight), sum(cat.weight), max(cat.weight), count(cat)
from eg.Cat cat
在select子句中,統(tǒng)計函數(shù)的變量也可以是集合。
select cat, count( elements(cat.kittens) )
from eg.Cat cat group by cat
下面是支持的統(tǒng)計函數(shù)列表:
avg(...), sum(...), min(...), max(...)
count(*)
count(...), count(distinct ...), count(all...)
distinct 和 all關鍵字的用法和語義與SQL相同。
select distinct cat.name from eg.Cat cat
select count(distinct cat.name), count(cat) from eg.Cat cat
11.6. 多態(tài)(polymorphism)查詢
類似下面的查詢:
from eg.Cat as cat
返回的實例不僅僅是Cat,也有可能是子類的實例,比如DomesticCat。Hibernate查詢可以在from子句中使用任何Java類或者接口的名字。查詢可能返回所有繼承自這個類或者實現(xiàn)這個接口的持久化類的實例。下列查詢會返回所有的持久化對象:
from java.lang.Object o
可能有多個持久化類都實現(xiàn)了Named接口:
from eg.Named n, eg.Named m where n.name = m.name
請注意,上面兩個查詢都使用了超過一個SQL的SELECT。這意味著order by子句將不會正確排序。(這也意味著你不能對這些查詢使用Query.scroll()。)
11.7. where子句
where子句讓你縮小你要返回的實例的列表范圍。
from eg.Cat as cat where cat.name=‘Fritz‘
返回所有名字為‘Fritz‘的Cat的實例。
select foo
from eg.Foo foo, eg.Bar bar
where foo.startDate = bar.date
會返回所有的滿足下列條件的Foo實例,它們存在一個對應的bar實例,其date屬性與Foo的startDate屬性相等。復合路徑表達式令where子句變得極為有力。思考下面的例子:
from eg.Cat cat where cat.mate.name is not null
這個查詢會被翻譯為帶有一個表間(inner)join的SQL查詢。如果你寫下類似這樣的語句:
from eg.Foo foo
where foo.bar.baz.customer.address.city is not null
你最終會得到的查詢,其對應的SQL需要4個表間連接。
=操作符不僅僅用于判斷屬性是否相等,也可以用于實例:
from eg.Cat cat, eg.Cat rival where cat.mate = rival.mate
select cat, mate
from eg.Cat cat, eg.Cat mate
where cat.mate = mate
特別的,小寫的id可以用來表示一個對象的惟一標識。(你可以使用它的屬性名。)
from eg.Cat as cat where cat.id = 123
from eg.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
又一次,第二個查詢不需要表間連接。
類似的,在存在多態(tài)持久化的情況下,特殊屬性class用于獲取某個實例的辨識值。在where子句中嵌入的Java類名將會轉換為它的辨識值。
from eg.Cat cat where cat.class = eg.DomesticCat
你也可以指定組件(或者是組件的組件,依次類推)或者組合類型中的屬性。但是在一個存在路徑的表達式中,最后不能以一個組件類型的屬性結尾。(這里不是指組件的屬性)。比如,假若store.owner這個實體的的address是一個組件
store.owner.address.city //okay
store.owner.address //error!
“任意(any)”類型也有特殊的id屬性和class屬性,這可以讓我們用下面的形式來表達連接(這里AuditLog.item是一個對應到<ant>的屬性)。
from eg.AuditLog log, eg.Payment payment
where log.item.class = ‘eg.Payment‘ and log.item.id = payment.id
注意上面查詢中,log.item.class和payment.class會指向兩個值,代表完全不同的數(shù)據(jù)庫字段。