4.3 使用HQL查詢
Hibernate提供了異常強大的查詢體系,使用Hibernate有多種查詢方式。可以選擇使用Hibernate的HQL查詢,或者使用條件查詢,甚至可以使用原生的SQL查詢語句,此外還提供了一種數據過濾功能,這些都可用于篩選目標數據。
下面分別介紹Hibernate的4種數據篩選方法:
4.3.1 HQL查詢
HQL是Hibernate Query Language的縮寫,HQL的語法很像SQL的語法,但HQL是一種面向對象的查詢語言。因此,SQL的操作對象是數據表和列等數據對象,而HQL的操作對象是類、實例、屬性等。
HQL是完全面向對象的查詢語言,因此可以支持繼承和多態等特征。
HQL查詢依賴于Query類,每個Query實例對應一個查詢對象。使用HQL查詢可按如下步驟進行:
(1)獲取Hibernate Session對象;
(2)編寫HQL語句;
(3)以HQL語句作為參數,調用Session的createQuery方法創建查詢對象;
(4)如果HQL語句包含參數,調用Query的setXxx方法為參數賦值;
(5)調用Query對象的list等方法遍歷查詢結果。
看下面的查詢示例:
public class HqlQuery
{
??? public static void main(String[] args)throws Exception
??? {
??????? HqlQuery mgr = new HqlQuery();
??????? //調用查詢方法
??????? mgr.findPersons();
??????? //調用第二個查詢方法
??????? mgr.findPersonsByHappenDate();
??????? HibernateUtil.sessionFactory.close();
??? }
??? //第一個查詢方法
??? private void findPersons()
??? {
??????? //獲得Hibernate Session
??????? Session sess = HibernateUtil.currentSession();
??????? //開始事務
??????? Transaction tx = sess.beginTransaction();
??????? //以HQL語句創建Query對象.
??????? //執行setString方法為HQL語句的參數賦值
??????? //Query調用list方法訪問查詢的全部實例
??????? List pl = sess.createQuery("from Person p where p.myEvents.title
??????? = :eventTitle")
??????????????????????? .setString("eventTitle","很普通事情")
??????????????????????? .list();
??????? //遍歷查詢的全部結果
??????? for (Iterator pit = pl.iterator() ; pit.hasNext(); )
??????? {
??????????? Person p = ( Person )pit.next();
??????????? System.out.println(p.getName());
??????? }
??????? //提交事務
??? ??? tx.commit();
??????? HibernateUtil.closeSession();
??? }
??? //第二個查詢方法
??? private void findPersonsByHappenDate()throws Exception
??? {
??????? //獲得Hibernate Session對象
??????? Session sess = HibernateUtil.currentSession();
??????? Transaction tx = sess.beginTransaction();
??????? //解析出Date對象
??????? SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
??????? Date start = sdf.parse("2005-01-01");
??????? System.out.println("系統開始通過日期查找人" + start);
??????? //通過Session的createQuery方法創建Query對象
??????? //設置參數
??????? //返回結果集
??????? List pl = sess.createQuery(
??????????? "from Person p where p.myEvents.happenDate between :firstDate
??????????? and :endDate")
??????????????????????? .setDate("firstDate",start)
??????????????????????? .setDate("endDate",new Date())
??????????????????????? .list();
??????? //遍歷結果集
??????? for (Iterator pit = pl.iterator() ; pit.hasNext(); )
??? ??? {
??????? ??? Person p = ( Person )pit.next();
??????????? System.out.println(p.getName());
??? ??? }
??????? tx.commit();
??????? HibernateUtil.closeSession();
??? }
}
通過上面的示例程序,可看出查詢步驟基本相似。Query對象可以連續多次設置參數,這得益于Hibernate Query的設計。
通常,setXxx方法的返回值都是void,但Hibernate Query的setXxx方法返回值是Query本身。因此,程序通過Session創建Query后,直接多次調用setXxx方法為HQL語句的參數賦值,再直接調用list方法返回查詢到的全部結果即可。
Query還包含兩個方法:
?? ● setFirstResult(int firstResult),設置返回的結果集從第幾條記錄開始。
?? ● setMaxResults(int maxResults),設置本次查詢返回的結果數。
這兩個方法用于實現Hibernate分頁。
下面簡單介紹HQL語句的語法。
HQL語句本身是不區分大小寫的。也就是說,HQL語句的關鍵字和函數都是不區分大小寫的。但HQL語句中所使用的包名、類名、實例名和屬性名都區分大小寫。
4.3.2 HQL查詢的from子句
from子句是最簡單的HQL語句,也是最基本的HQL語句。from關鍵字后緊跟持久化類的類名。例如:
from Person
表明從Person持久化類中選出全部的實例。
大部分時候,推薦為該Person的每個實例起別名。例如:
from Person as p
在上面的HQL語句中,Person持久化類中的實例的別名為p,既然 p是實例名,因此也應該遵守Java的命名規則:第一個單詞的首字母小寫,后面每個單詞的首字母大寫。
命名別名時,as關鍵字是可選的,但為了增加可讀性,建議保留。
from后還可同時出現多個持久化類,此時將產生一個笛卡兒積或跨表的連接。
4.3.3 HQL查詢的select子句
select子句用于確定選擇出的屬性,當然select選擇的屬性必須是from后持久化類包含的屬性。例如:
select p.name from Person as p
select可以選擇任意屬性,不僅可以選擇持久化類的直接屬性,還可以選擇組件屬性包含的屬性,例如:
select p.name.firstName from Person as p
select也支持將選擇出的屬性存入一個List對象中,例如:
select new list(p.name , p.address) from Person as p
甚至可以將選擇出的屬性直接封裝成對象,例如:
select new ClassTest(p.name , p.address) from Person as p
前提是ClassTest支持p.name和p.address的構造器,假如p.name的數據類型是?????????? String,p.address的數據類型是String,則ClassTest必須有如下的構造器:
ClassTest(String s1, String s2)
select還支持給選中的表達式命名別名,例如:
select p.name as personName from Person as p
這種用法與new map結合使用更普遍。如:
select new map(p.name as personName) from Person as p
在這種情形下,選擇出的是Map結構,以personName為key,實際選出的值作為value。
4.3.4 HQL查詢的聚集函數
HQL也支持在選出的屬性上,使用聚集函數。HQL支持的聚集函數與SQL完全相同,有如下5個:
?? ● avg,計算屬性平均值。
?? ● count,統計選擇對象的數量。
?? ● max,統計屬性值的最大值
?? ● min,統計屬性值的最小值。
?? ● sum,計算屬性值的總和。
例如:
select count(*) from Person
select max(p.age) from Person as p
select子句還支持字符串連接符、算術運算符以及SQL函數。如:
select p.name || "" || p.address from Person as p
select子句也支持使用distinct和all關鍵字,此時的效果與SQL中的效果完全相同。
4.3.5 多態查詢
HQL語句被設計成能理解多態查詢,from后跟的持久化類名,不僅會查詢出該持久化類的全部實例,還會查詢出該類的子類的全部實例。
如下面的查詢語句:
from Person as p
該查詢語句不僅會查詢出Person的全部實例,還會查詢出Person的子類,如Teacher的全部實例,前提是Person和Teacher完成了正確的繼承映射。
HQL支持在from子句中指定任何Java類或接口,查詢會返回繼承了該類的持久化子類的實例或返回實現該接口的持久化類的實例。下面的查詢語句返回所有被持久化的對象:
from java.lang.Object o
如果Named接口有多個持久化類,下面的語句將返回這些持久化類的全部實例:
from Named as n
注意:后面的兩個查詢將需要多個SQL SELECT語句,因此無法使用order by子句對結果集進行排序,從而,不允許對這些查詢結果使用Query.scroll()方法。
4.3.6 HQL查詢的where子句
where子句用于篩選選中的結果,縮小選擇的范圍。如果沒有為持久化實例命名別名,可以直接使用屬性名引用屬性。
如下面的HQL語句:
from Person where name like 'tom%'
上面HQL語句與下面的語句效果相同:
from Person as p where p.name like "tom%"
在后面的HQL語句中,如果為持久化實例命名了別名,則應該使用完整的屬性名。兩個HQL語句都可返回name屬性以tom開頭的實例。
復合屬性表達式加強了where子句的功能,例如如下HQL語句:
from Cat cat where cat.mate.name like "kit%"
該查詢將被翻譯成為一個含有內連接的SQL查詢,翻譯后的SQL語句如下:
select * from cat_table as table1 cat_table as table2 where table1.mate =
table2.id and table1.name like "kit%"
再看下面的HQL查詢語句:
from Foo foo where foo.bar.baz.customer.address.city like"guangzhou%"
翻譯成SQL查詢語句,將變成一個四表連接的查詢。
=運算符不僅可以被用來比較屬性的值,也可以用來比較實例:
from Cat cat, Cat rival where cat.mate = rival.mate
select cat, mate
from Cat cat, Cat mate
where cat.mate = mate
特殊屬性(小寫)id可以用來表示一個對象的標識符。(也可以使用該對象的屬性名。)
from Cat as cat where cat.id = 123
from Cat as cat where cat.mate.id = 69
第二個查詢是一個內連接查詢,但在HQL查詢語句下,無須體會多表連接,而完全使用面向對象方式的查詢。
id也可代表引用標識符。例如,Person類有一個引用標識符,它由country屬性 與medicareNumber兩個屬性組成。
下面的HQL語句有效:
from Person as person
where person.id.country = 'AU'
??? and person.id.medicareNumber = 123456
from Account as account
where account.owner.id.country = 'AU'
??? and account.owner.id.medicareNumber = 123456
第二個查詢跨越兩個表Person和Account。是一個多表連接查詢,但此處感受不到多表連接查詢的效果。
在進行多態持久化的情況下,class關鍵字用來存取一個實例的鑒別值(discriminator value)。嵌入where子句中的Java類名,將被作為該類的鑒別值。例如:
from Cat cat where cat.class = DomesticCat
where子句中的屬性表達式必須以基本類型或java.lang.String結尾,不要使用組件類型屬性結尾,例如Account有Person屬性,而Person有Name屬性,Name有firstName屬性。
看下面的情形:
from Account as a where a.person.name.firstName like "dd%" //正確
from Account as a where a.person.name like "dd%" //錯誤
4.3.7 表達式
HQL的功能非常豐富,where子句后支持的運算符異常豐富,不僅包括SQL的運算符,還包括EJB-QL的運算符等。
where子句中允許使用大部分SQL支持的表達式:
?? ● 數學運算符+、–、*、/?等。
?? ● 二進制比較運算符=、>=、<=、<>、!=、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等。
?? ● 字符串連接符value1 || value2或使用字符串連接函數concat(value1 , value2)。
?? ● 時間操作函數current_date()、current_time()、current_timestamp()、second()、minute()、hour()、day()、month()、year()等。
?? ● HQL還支持EJB-QL 3.0所支持的函數或操作substring()、trim()、lower()、upper()、length()、locate()、abs()、sqrt()、bit_length()、coalesce()和nullif()等。
?? ● 還支持數據庫的類型轉換函數,如cast(... as ...),第二個參數是Hibernate的類型名,或者extract(... from ...),前提是底層數據庫支持ANSI cast()?和extract()。
?? ● 如果底層數據庫支持如下單行函數sign()、trunc()、rtrim()、sin()。則HQL語句也完全可以支持。
?? ● HQL語句支持使用?作為參數占位符,這與JDBC的參數占位符一致,也可使用命名參數占位符號,方法是在參數名前加冒號 :,例如 :start_date和:x1等。
?? ● 當然,也可在where子句中使用SQL常量,例如'foo'、69、'1970-01-01 10:00:???????? 01.0'等。
?? ● 還可以在HQL語句中使用Java public static final 類型的常量,例如eg.Color.TABBY。
除此之外,where子句還支持如下的特殊關鍵字用法。
?? ● in與between...and可按如下方法使用:
from DomesticCat cat where cat.name between 'A' and 'B'
from DomesticCat cat where cat.name in ( 'Foo','Bar','Baz')
?? ● 當然,也支持not in和not between...and的使用,例如:
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可以被用來測試空值,例如:
from DomesticCat cat where cat.name is null;
from Person as p where p.address is not null;
如果在Hibernate配置文件中進行如下聲明:
<property name="hibernate.query.substitutions">true 1, false 0</property>
上面的聲明表明,HQL轉換SQL語句時,將使用字符1和0來取代關鍵字true和false。然后將可以在表達式中使用布爾表達式,例如:
from Cat cat where cat.alive = true
?? ● 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、exists、in操作集合里的元素,例如:
//操作集合元素
select mother from Cat as mother, Cat as kit
where kit in elements(foo.kittens)
//p的name屬性等于集合中某個元素的name屬性
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子句中使用。
?? ● where子句中,有序集合的元素(arrays, lists, maps)可以通過[ ]運算符訪問。例如:
//items是有序集合屬性,items[0]代表第一個元素
from Order order where order.items[0].id = 1234
//holidays是map集合屬性,holidays[national day]代表其中一個元素
select person from Person person, Calendar calendar
where calendar.holidays['national day'] = person.birthDay
and person.nationality.calendar = calendar
//下面同時使用list 集合和map集合屬性
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,可以大大簡化選擇語句的書寫,提高查詢語句的可讀性,看下面的HQL語句:
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)
如果翻譯成SQL語句,將變成如下形式:
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
??? )
4.3.8 order by子句
查詢返回的列表(list)可以根據類或組件屬性的任何屬性進行排序,例如:
from Person as p
order by p.name, p.age
還可使用asc或desc關鍵字指定升序或降序的排序規則,例如:
from Person as p
order by p.name asc , p.age desc
如果沒有指定排序規則,默認采用升序規則。即是否使用asc關鍵字是沒有區別的,加asc是升序排序,不加asc也是升序排序。
4.3.9 group by子句
返回聚集值的查詢可以對持久化類或組件屬性的屬性進行分組,分組所使用的group by子句。看下面的HQL查詢語句:
select cat.color, sum(cat.weight), count(cat)
from Cat cat
group by cat.color
類似于SQL的規則,出現在select后的屬性,要么出現在聚集函數中,要么出現在group by的屬性列表中。看下面示例:
//select后出現的id出現在group by之后,而name屬性則出現在聚集函數中
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)
注意:having子句用于對分組進行過濾,因此having子句只能在有group by子句時才可以使用,沒有group by子句,不能使用having子句。
Hibernate的HQL語句會直接翻譯成數據庫SQL語句。因此,如果底層數據庫支持的having子句和group by子句中出現一般函數或聚集函數,HQL語句的having子句和order by 子句中也可以出現一般函數和聚集函數。
例如:
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
注意:group by子句與 order by子句中都不能包含算術表達式。
4.3.10 子查詢
如果底層數據庫支持子查詢,則可以在HQL語句中使用子查詢。與SQL中子查詢相似的是,HQL中的子查詢也需要使用()括起來。如:
from Cat as fatcat
where fatcat.weight > ( select avg(cat.weight) from DomesticCat cat )
如果select中包含多個屬性,則應該使用元組構造符:
from Cat as cat
where not ( cat.name, cat.color ) in (
??? select cat.name, cat.color from DomesticCat cat
)
4.3.11 fetch關鍵字
對于集合屬性,Hibernate默認采用延遲加載策略。例如,對于持久化類Person,有集合屬性scores。加載Person實例時,默認不加載scores屬性。如果Session被關閉,Person實例將無法訪問關聯的scores屬性。
為了解決該問題,可以在Hibernate映射文件中取消延遲加載或使用fetch join,例如:
from Person as p join p.scores
上面的fetch語句將會初始化person的scores集合屬性。
如果使用了屬性級別的延遲獲取,可以使用fetch all properties來強制Hibernate立即抓取那些原本需要延遲加載的屬性,例如:
from Document fetch all properties order by name
from Document doc fetch all properties where lower(doc.name) like '%cats%'
4.3.12 命名查詢
HQL查詢還支持將查詢所用的HQL語句放入配置文件中,而不是代碼中。通過這種方式,可以大大提供程序的解耦。
使用query元素定義命名查詢,下面是定義命名查詢的配置文件片段:
<!-- 定義命名查詢 -->
<query name="myNamedQuery">
??? <!-- 此處確定命名查詢的HQL語句 -->
??? from Person as p where p.age > ?
</query>
該命名的HQL查詢可以直接通過Session訪問,調用命名查詢的示例代碼如下:
private void findByNamedQuery()throws Exception
{
??? //獲得Hibernate Session對象
??? Session sess = HibernateUtil.currentSession();
??? //開始事務
??? Transaction tx = sess.beginTransaction();
??? System.out.println("執行命名查詢");
??? //調用命名查詢
??? List pl = sess.getNamedQuery("myNamedQuery")
??????????????????????? //為參數賦值
??????????????????? ?? .setInteger(0 , 20)
??????????????????????? //返回全部結果
??????????????????? ?? .list();
??? //遍歷結果集
??? for (Iterator pit = pl.iterator() ; pit.hasNext(); )
??? {
??????? Person p = ( Person )pit.next();
??????? System.out.println(p.getName());
??? }
??? //提交事務
??? tx.commit();
??? HibernateUtil.closeSession();
}
posted on 2009-07-19 08:48
jadmin 閱讀(1603)
評論(0) 編輯 收藏