Hibernate检索1
Hibernate 的關聯關系,通過關聯關系能夠對數據庫表進行簡單的操作。在大多數應用中,查詢屬于最重要的部分,而目前我們只能使用get方法和 load方法進行簡單的查詢,本章將主要講解Hibernate的查詢操作。?
Hibernate 支持兩種主要的查詢方式。強大且易于使用的面向對象查詢語言 HQL(Hibernate Query Language)。和Criteria查詢。以及使用原生 SQL (native SQL)描述 Hibernate 查詢。其中HQL是使用較為廣泛的方式,也是我們本章學習的重點。
核心技能部分
1.1??Hibernate檢索
在Hibernate 中,檢索對象的方式包括:
(1)導航圖:根據已經加載的對象,導航到其他對象。例如,對于已經加載的Customer對象,調用它的getOrders().iterator()方法就可以導航到所有關聯的Order對象,假如在關聯級別使用了延遲加載檢索策略,那么首次執行此方法時,Hibernate會從數據庫中加載關聯的Order對象,否則就從緩存中取得Order對象。?
(2)OID:按照對象的OID來檢索對象。Session的get()和load()方法提供了這種功能。如果在應用程序中事先知道了OID,就可以使用這種檢索對象的方式。
(3)HQL:Hibernate Query Language,它是完全面向對象的查詢語句,查詢功能非常強大,具備繼承、多態和關聯等特性。Hibernate官方推薦使用HQL進行查詢。
(4)QBC:Query By Criteria,以對象的方式進行查詢,將查詢語句封裝為對象操作。優點:可讀性好,符合Java 程序員的編碼習慣。缺點:不夠成熟,不支持投影(projection)或統計函數(aggregation)。
(5)本地查詢:使用本地數據庫的SQL查詢語句。
1.2?HQL簡介
1.2.1?為什么使用HQL
現在我們回憶一下我們在前一章學習中所遇到的查詢問題,如何查詢所有的版塊?如何查詢指定標題的帖子?顯然是用我們已經掌握的get 或者load 這樣的以id為條件進行查詢的方式是無法做到的,是用HQL就可以輕松解決這樣的問題。
現在我們使用HQL來檢索所有的名稱為鬼吹燈的帖子,代碼如示例4.1所示。
示例4.1
@Testpublic void hqlQuery1() throws Exception {String hql="from Thread as t where t.title like '%鬼吹燈%'";Query query = session.createQuery(hql);List<Thread> list = query.list();for(Thread t : list){System.out.println("帖子名稱:"+t.getTitle()+" 內容:"+t.getContent());}我們再回憶一下使用JDBC完成這一查詢的情況,jdbc是面向數據庫表的查詢,查詢出來的是一行行數據 一個一個的字段,還需要手工進行繁瑣的數據提取和封裝,才能得到我們需要的對象集合,而使用HQL則可以避免JDBC的這些弊端,提供了更簡便和強大的對象化查詢能力,而且HQL還是獨立與數據庫的,HQL語句不與具體數據庫產品耦合。HQL具有以下功能:
(1)支持在查詢語句中設定查詢條件,動態綁定參數。
(2)支持投影查詢。
(3)支持分頁查詢。
(4)支持連接查詢。
(5)支持分組查詢,能夠使用關鍵字having和group by。
(6)內置聚集函數,如sum()、min()、max()等。
(7)可以調用用戶自定義函數。
(8)支持子查詢。
1.2.2?HQL入門
從示例4.1中我們可以總結出使用HQL的四個步驟:
1.?獲取session。
2.?編寫HQL語句。
3.?創建Query對象。
4.?執行查詢,獲取結果。
HQL的完整的語法:
?
[select/update/delete...][from...][where...][ group by...][having...]?[ order by . . . ]
HQL的語法和SQL非常的相似,但是不要被語法結構上的相似所迷惑,HQL具有鮮明的面向對象查詢的特征,HQL是非常有意識的被設計為完全面向對象的查詢,它可以理解如繼承、多態 和關聯之類的概念。在上例"from Thread as ?t ?where t.title ?like '%鬼吹燈%'"??中 Thread是類名而不是表名,title是Thread的屬性名而不是字段名,查詢出的也都是對象,在HQL的世界里沒有數據庫、沒有表字段,有的只是對象。這就是HQL和SQL的本質區別,也是需要重點理解和掌握的。
HQL還有一些基本的語法規則需要了解:
?HQL語句的關鍵字不區分大小寫,但推薦小寫。
?HQL中出現的類名,屬性名嚴格區分大小寫。
?可以為類設置別名,以供其他地方引用,例如 上例中的 as t。
?as 關鍵字是可選的, 一般別名推薦小寫。
?from前也可以加select 但必須配合別名使用。
HQL非常的強大與復雜。實際上,Hibernate的一個主要賣點就是查詢語句的威力。下面我們會向大家一一介紹。
1.3?實體查詢
根據上述HQL的查詢步驟,編寫一個最簡單的查詢,代碼如示例4.2所示。
示例4.2
String hql = “from Thread”;Query query = session.createQuery(hql);List< Thread> List = query. list ( );for ( Thread t : List ) {System.out.println("名稱:”+t getTitle());}上述代碼中,hql = “from Thread”將取出?Thread對象所對應表的全部記錄,對應的?SQL語句為:?Select * from ?tip。
HQL還支持多態查詢,如果實體之間存在繼承關系,那么from Thread將會查詢出所有的Thread和他的子類數據。例如 from java.lang.Object 將會返回所有的數據庫表數據。
與?SQL語句相同,HQL也支持where子句。例如示例4.1中的"from Thread as ?t ?where t.title ?like '%鬼吹燈%'"。
Hibernate 的?HQL查詢支持與SQL相同的各種運算,詳見表 4-1-1所示。
表 4-1-1???HQL 支持的各種運算
運算類型 | HQL運算符 | 含義 |
比較運算 | = | 等于 |
<> | 不等于 | |
> | 大于 | |
>= | 大于等于 | |
< | 小于 | |
<= | 小于等于 | |
Is null | 為空 | |
Is not null | 不為空 | |
范圍運算 | In | 等于列表中的某一個值 |
Not in | 不能與列表中的任意一個值 | |
Between value1 and value2 | 大于等于值1,小于等于值2 | |
Not between value1 and value2 | 小于值1或者大于值2 | |
字符串模式匹配 | Like | 字符串模式匹配 |
邏輯運算 | and | 邏輯與 |
Or | 邏輯或 | |
Not | 邏輯非 |
我們可以通過這些操作符為where子句指定條件,也可以通過and 、or等邏輯連接符組合各個條件,代碼如示例4.3所示。
示例4.3
@Testpublic void hqlQuery2() throws Exception {String hql="from Thread as t where t.title like '%鬼吹燈%' and t.dateCreated>'2010-07-10'";Query query = session.createQuery(hql);List<Thread> list = query.list();for(Thread t : list){System.out.println("帖子名稱:"+t.getTitle()+" 內容:"+t.getContent());}}這些操作符的使用和SQL都沒什么區別,只需要把握HQL面向對象的精神即可,這里不再贅述。
1.4?屬性查詢
現在我們使用HQL可以輕松的查詢到我們需要的對象,但在某些情況下,我們并不需要取得完整的實體對象,如在下拉列表中顯示出版塊的名稱,或者在加載帖子列表的時候,我們只需要帖子的標題等信息 而例如content這樣比較大的屬性并不需要加載,此時我們需要的數據可能僅僅是對象的某一個或者某幾個屬性,通過HQL我們可以輕松的做到這一點!代碼如示例4.4所示。
示例4.4
@Testpublic void hqlQuery3() throws Exception {String hql="select t.title from Thread as t";Query query = session.createQuery(hql);List<String> list= query.list();for(String title: list){System.out.println("帖子標題:"+title);}}單一屬性查詢,返回結果集屬性列表,元素類型和實體類中相應的屬性類型一致,但如果是對多個屬性的查詢HQL查詢返回的結果又是什么呢?,多個屬性的查詢如示例4.5所示。
示例4.5
@Testpublic void hqlQuery3() throws Exception {String hql="select t.title,t.dateCreated from Thread as t";Query query = session.createQuery(hql);List<Object[]> list = query.list();for(Object[] objects: list){System.out.println("帖子標題:"+objects[0]+" 發布時間:"+objects[1]);}}多個屬性查詢,返回的集合元素是對象數組,數組元素的類型和對應的屬性在實體類中的類型一致,數組的長度取決于select中屬性的個數。
另外還有一種更加面向對象化的屬性查詢方式 如示例4.6所示。
示例4.6
@Testpublic void hqlQuery5() throws Exception {String hql="select new Thread(th.title,th.hit) from Thread as th";Query query = session.createQuery(hql);List<Thread> list = query.list();for(Thread t: list){System.out.println("標題:"+t.getTitle()+" 點擊量:"+t.getHit());}}這種方式能夠在HQL語句中動態的構造對象,并封裝我們所需要的屬性數據,這時候list中保持的是對象,使用這種方式需要注意以下兩點:
?類必須有相應的構造函數。
?除了在構造的時候賦予的屬性值外,其他屬性都是null。
?在MySQL數據庫中如果是日期類型,譬如:dateCreated,實體類的屬性應是java.util.Date,在構造方法中對其賦值。MyEclipse生成的是java.sql.Timestamp類型的。
1.5?參數綁定
在前面的應用中,我們的查詢條件都是直接在HQL中表達,例如:select info from Thread as info where info.goodsName='手機',在實際應用中查詢的條件肯定是在不斷變化的,解決這個問題的最直接辦法就是拼接字符串,但這種方式很容易遭到SQL注入的攻擊,安全性很差而且執行效率低?,F在我們回憶一下在JDBC中是如何解決這個問題的?
在JDBC中我們是使用PrepareStatement對象進行了參數的動態綁定,HQL也提供了類似的參數綁定方式。
HQL中的參數綁定主要有三種形式:
?按位置綁定。
?按名稱綁定。
?封裝參數。
4.5.1 按照位置綁定
代碼如示例4.7所示。
示例4.7
@Testpublic void hqlQuery6() throws Exception {String hql="from Thread as th where th.topped=? and th.title like ?";Query query = session.createQuery(hql);query.setBoolean(0,true);query.setString(1, "%鬼吹燈%");List<Thread> list = query.list();for(Thread th: list){System.out.println("標題:"+th.getTitle()+" 置頂:"+th.getTopped());}}使用這種方式綁定參數需要注意如下幾點:
?必須按照參數的順序,調用相應的setType()方法賦值。
?參數的下標從0開始。
?如果有多個參數的時候,必須保證每個參數都被綁定值。
4.5.2 按照名稱綁定
代碼如示例4.8所示。
示例4.8
@Testpublic void hqlQuery7() throws Exception {String hql="from Thread as th where th.topped=:top and th.title like :name";Query query = session.createQuery(hql);query.setBoolean("top",true);query.setString("name","%鬼吹燈%");List<Thread> list = query.list();for(Thread th: list){System.out.println("標題:"+th.getTitle()+" 置頂:"+th.getTopped());}}使用按照名稱進行綁定,請注意以下幾點:
?在HQL查詢語句中定義命名參數時以?“:”開頭。
?Query提供的方法能綁定各種類型的參數。此類?setXXX()方法中,第一個參數用于設置各種類型的命名參數,第二個參數表示命名參數的值。
這種方式有比較好的可讀性,可以避免因大意而產生的參數順序錯誤。
4.5.3 封裝參數
當需要綁定的參數非常多,那么無論使用按位置,還是按名稱綁定參數都會非常的繁瑣,HQL中提供了第三種方式進行參數的綁定。即先將參數值封裝到bean中,然后將bean與Query進行綁定。代碼如示例4.9所示。
示例4.9
@Testpublic void hqlQuery8() throws Exception {String hql="from Thread as th where th.title like :title and "+"th.hit between:low_hit and :high_hit and th.replyCount>:rcount and th.topped=:top";Query query = session.createQuery(hql);Map queryProperties = new HashMap();queryProperties.put("title","%java%");queryProperties.put("high_hit",100000);queryProperties.put("low_hit",10);queryProperties.put("rcount",10);queryProperties.put("top",false);query.setProperties(queryProperties);List<Thread> list = query.list();for(Thread th: list){System.out.println("標題:"+th.getTitle()+" 點擊量:"+th.getHit()+" 回帖:"+th.getReplyCount());}}這種方式一定要注意封裝參數值的bean中的屬性名一定要和HQL語句中的命名參數名稱一致,這樣Hibernate才能根據名稱進行匹配。
1.6?使用聚合函數
HQL中聚合函數的使用和SQL中基本一致。
1.6.1?count()
查詢指定用戶所發布的帖子總數,代碼如示例4.10所示。
示例4.10
@Testpublic void hqlQuery9() throws Exception {String hql="select count(th) from Thread as th where th.author.id=1";Query query = session.createQuery(hql);Long count=(Long) query.uniqueResult();System.out.println("用戶發帖: "+count);}本例中使用了count()函數來計算總數,顯然查詢的結果不肯能有多個,這個時候再使用list方法求的集合,并從中取得數據,就顯得很繁瑣,這時候可以使用uniqueResult()來取得唯一結果,這里需要注意的是,如果返回的結果大于1個本方法將會拋出異常。
1.6.2?max()和 min()
求得所有帖子中最大,最小點擊量。代碼如示例4.11所示。
示例4.11
@Testpublic void hqlQuery10() throws Exception {String hql="select max(th.hit),min(th.hit)from Thread as th";Query query = session.createQuery(hql);List<Object[]> list = query.list();Object[] object = list.get(0);System.out.println("單貼最高點擊: "+object[0]);System.out.println("單貼最低點擊: "+object[1]);}1.6.3?avg()和 sum()
求userid=1的用戶所發布信息的總點擊量和平均點擊量,代碼如示例4.12所示。
示例4.12
@Testpublic void hqlQuery11() throws Exception {String hql="select avg(th.hit),sum(th.hit)from Thread as th where th.author.id=1";Query query = session.createQuery(hql);List<Object[]> list = query.list();Object[] object = list.get(0);System.out.println("平均點擊量: "+object[0]);System.out.println("總點擊量: "+object[1]);}1.7?排序
與?SQL相同,HQL可以通過order by子句實現對查詢結果的排序,代碼如下。
??from ?Thread ?as ?th??order ?by ?th.datecreated??desc;
order by子句可以指定多個排序條件,測試代碼如下。
?from Thread ?as ?th ?order by th?. hit ,th. datecreated?desc;
1.8?分組
HQL也可以像SQL一樣使用group by子句進行分組統計,例如 我們根據帖子類型進行數量統計。代碼如示例4.13所示。
示例4.13
@Testpublic void hqlQuery12() throws Exception {String hql = "select count (th) , th.type from Thread as th group by th.type";Query query = session.createQuery(hql);List< Object[]> list = query.list();for(Object[] objects : list){System.out.println( "數量:"+objects[0]+"類型: "+objects[1] );}}having子句可以對group by子句進行甄選。例如,對帖子數量大于8的種類的分類統計,代碼如示例4.14所示。
示例4.14
@Testpublic void hqlQuery13() throws Exception {String hql = "select count (th) , th.type from Thread as th group by th.type having count(th)>8";Query query = session.createQuery(hql);List< Object[]> list = query.list();for(Object[] objects : list){System.out.println( "數量:"+objects[0]+"類型: "+objects[1] );}}1.9?分頁查詢
使用JDBC進行數據庫的分頁查詢的語句很復雜,而且每種數據庫產品都有特有的分頁語句,程序與具體數據庫綁定,造成系統的移植和擴展困難,而Hibernate為我們提供了簡便、統一的分頁方式,主要通過Query的以下兩個方法實現:
??setFirstResult(int firstResult):設定從哪一個對象開始檢索,參數firstResult表示這個對象在查詢結果中的索引位置,索引位置的起始值為0。默認情況下,Query接口從查詢結果中的第一個對象,也就是索引位置為0的對象開始檢索。
??setMaxResult(int maxResults):設定一次最多檢索出的對象數目。默認情況下,Query接口檢索出查詢結果中所有的對象。
具體代碼如示例4.15所示。
示例4.15
Query query =session.createQuery("from?Thread ?as th?");
query.setFirstResult(0);
query.setMaxResults(10);
List result = query.list();
?
1.10?子查詢
對于支持子查詢的數據庫,Hibernate支持在查詢中使用子查詢。他可以在查詢中使用另外一條查詢的結果,一個子查詢必須出現在where子句中且被圓括號包圍起來(經常是SQL聚集函數的圓括號)甚至相互關聯的子查詢(引用到外部查詢中的別名的子查詢)也是允許的。
查詢點擊量最大的帖子。代碼如示例4.16所示。
示例4.16
@Test
public void hqlQuery14() throws Exception {
String hql = "select th from Thread as th where th.hit=(select max(hit) from Thread)";
????Query query = session.createQuery(hql);
????List< Thread> list = query.list();
????for(Thread th : list){
???? ??System.out.println( "點擊量:"+th.getHit()+"帖子標題: "+th.getTitle() );
????}
}
運行后控制臺輸出如圖4.1.11所示。
?
圖4.1.11 子查詢
本章總結
?
??Hibernate 中,檢索對象的方式
n?導航圖:根據已經加載的對象,導航到其他對象。
n?OID:按照對象的OID來檢索對象。Session的get()和load()方法
n?HQL:Hibernate Query Language
n?QBC:Query By Criteria
n?本地查詢:使用本地數據庫的SQL查詢語句。
?HQL
n?HQL實體查詢
n?HQL屬性查詢
n?HQL參數綁定
n?HQL實現查詢排序
n?HQL實現查詢分組
n?HQL實現查詢分頁
選擇題
1. setMaxResults(3)方法中,參數值3是指( )。
????A. 從第?3條記錄開始
????B. 從第4條記錄開始
????C. 查詢?3條記錄
????D. 查詢4條記錄
2.?在Hibernate 中,下面代碼實現了對Book實體中title屬性的模糊查詢說法正確的是?( )。
Session session=this.getSession ( );??????????????????????
String ?hql=”from ?Book ?model ?where ?modeL. title like?”;??// (1)
Query query =session.createQuery (hql );// (2)
query.setString(0,“%張明%”);// (3)
List list = query.list ( );// (4)
A. 第(1)行中,Book與?model之間必須有as關鍵字
B. 第(2)行中沒有錯誤
C. 第(3)行應該為:query.setString(0,“張明”);
D. 第(4)行應該為:List llist = query.executeQuery();
3. 關于HQL查詢,下面說法中錯誤的是()。
????A. HQL查詢的?select子句中必須區分大小寫
????B. HQL支持統計函數
????C. HQL支持僅查詢對象的某幾個屬性,查詢結果保存于Object數組中
????D. HQL語句可以實現類似PreparedStatement 的效果
4. 下面代碼的執行效果是()。
String hql=”from Student s order by s.score asc”;
Query query = session.createQuery(hql);
query.setFirstResult(0);
query.setMaxResult(5);
return query.list();
A.返回分數最高的五個學生。
B. ?返回分數最高的六個學生。
C.返回分數最低的五個學生。
D. ?返回分數最低的六個學生。
5. 下面HQL語句的含義是()。
Select stu ?from Student stu where stu.score>(select avg(score) from Student)
A. 查詢所有學生的平均分。
B. ??查詢得分大于平均分的學生成績。
C. 查詢得分最高的學生。
D. ? 查詢得分大于平均分的學生
上機練習
在上機的基礎上完成對帖子的回復功能 ,點擊帖子可以分頁顯示帖子的詳細內容和他的所有回復,并在添加回復后及時更新帖子的最后回復時間和版塊的最后回復。
總結
以上是生活随笔為你收集整理的Hibernate检索1的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Hibernate 注解配置
- 下一篇: 大数据 机器学习 算法概论