HQL查詢語言

——HQL查詢語言
 
示例:Hibernate3_10_HQL Hibernate查詢:
    數據查詢與檢索是Hibernate中的一個亮點。相對其他ORM實現而言,Hibernate提供了靈活多樣的查詢機制。
標準化對象查詢(Criteria Query):以對象的方式進行查詢,將查詢語句封裝爲對象操作。優點:可讀性好,符合Java 程序員的編碼習慣。缺點:不夠成熟,不支持投影(projection)或統計函數(aggregation)
Hibernate語言查詢(Hibernate Query Language,HQL):它是完全面向對象的查詢語句,查詢功能非常強大,具備多態、關聯等特性 。Hibernate官方推薦使用HQL進行查詢。 Native SQL Queries(原生SQL查詢):直接使用標準SQL語言或跟特定數據庫相關的SQL進行查詢。 
Hibernate語言查詢(Hibernate Query Language,HQL): HQL用面向對象的方式生成SQL語句:
以類和屬性來代替表和數據列; 支持多態; 支持各種關聯; 減少了SQL的冗餘;
HQL支持所有的關係數據庫操作:
連接(joins,包括Inner/outer/full joins),笛卡爾積(cartesian products); 投影(projection)--查詢一到多個屬性;
聚合(Aggregation,max, avg)和分組(group); 排序(Ordering); 子查詢(Subqueries);
SQL函數(SQL function calls)
簡單示例:查詢用戶名以“王”開頭的所有用戶。   Query query = session.createQuery(
 "from User user where user.name like '王%'");   List users = query.list();
複雜示例:從User和Group中查找屬於“admin”組的所有用戶。     Query query = session.createQuery(
              "from User user where user.group.name='admin'"); 如果用傳統的SQL則查詢語句如下:
select user.userId as userId, user.name as name, user.groupId as groupId, user.idCardId as idCardId  from TBL_USER user, TBL_GROUP group where (group.groupName='admin'  and user.groupId=group.groupId) 
HQL語言-簡單屬性查詢: (1)單一屬性查詢:
   List users = session.createQuery("select name from User").list(); 說明:
    List集合中此時存儲的是name屬性集合,其類型爲實體類(即User)的name屬性的類型(本例爲String類型),所以List集合中對象的類型取決於實體類中屬性的類型。 
(2)多屬性查詢:    List users = 
        session.createQuery("select id, name from User ").list(); 說明:
    List集合中此時存儲的對象數組(即Hibernate查詢多個屬性時返回的是對象數組,一條記錄爲一個對象數組,最後將所有對象數組存儲到集合中),其中對象數組的長度與查詢的屬性個數是相同的(即select關鍵字後面有多少個屬性,將來對象數組的長度就是多少);
    爲什麼會是Object類型的數組?這是因爲實體類中屬性的類型是不同的,所以用Object類型表示更爲通用。
    數組下標元素存儲的對象類型與實體類中屬性的類型相同。 
(3)採用HQL動態實例化對象方式返回所要查詢的屬性:  List users =
  session.createQuery("select new User(id, name) from User").list(); 說明:
    List集合中此時存儲的是User對象,但是該User對象並不是實體對象,而是我們自己new出來的(即不是Hibernate自動封裝的),因此不受Session統一管理,此時的User對象中只有id和name屬性有值,其它屬性均爲null。 User對象必須提供一個構造函數,形式爲:     public User(int id, String name) {„}
否則將不能創建User對象,因爲創建對象時調用的是帶參數的構造方法。 
(4)採用別名的方式查詢屬性值:
    採用別名方式查詢屬性值,在Hibernate中可以使用as關鍵字指定別名,也可以不使用as關鍵字指定別名,兩者都可以指定別名,如下所示: 不採用as關鍵指定別名       List users = 
     session.createQuery("select u.id, u.name from User u").list(); 採用as關鍵字指定別名        List users = 
         session.createQuery("select u.id, u.name from User as u").list();
說明:當指定別名後,在查詢的屬性名需要使用前綴時必須使用別名,而不能再使用原實體對象的名稱,即: u.id, u.name。 
【小結】
單一屬性查詢,返回結果集屬性列表,元素類型和實體類中相應的屬性類型一致; 多個屬性查詢,返回的集合元素是對象數組,數組元素的類型和對應的屬性在實體類中的類型一致;
數組的長度取決與select中屬性的個數;
如果認爲返回數組不夠對象化,可以採用HQL動態實例化對象的方式:       select new User(id, name) from User 
HQL語言-實體對象查詢: 查詢實體對象
   List students = session.createQuery("from User").list(); 說明:
    此時List集合中存儲的User對象爲持久狀態,並納入了Session的管理,當User對象的屬性發生變化時,在提交事務後會發出相應的update語句與數據庫同步。 如:
if(user.getId() == 1){ user.setName("AAAA");
//會發出update語句,因爲此時的user對象是實體對象,由於名字發生了變化,所以此時在提交事務後會發出相應的update語句與數據庫同步 session.getTransaction().commit(); } 
使用別名查詢實體對象-忽略select關鍵字
    採用別名方式查詢屬性值,在Hibernate中可以使用as關鍵字指定別名,也可以不使用as關鍵字指定別名,兩者都可以指定別名,如下所示: 不採用as關鍵指定別名
    List users = session.createQuery("from User u").list(); 採用as關鍵字指定別名
    List users = session.createQuery("from User as u").list(); 
如:
List students = session.createQuery("select u.id, u.name from User u where CONVERT(varchar(10), DATEPART(mm,u.create_time))=?")    .setParameter(0, "06")             .list(); 
(6) 查詢某一時間段的數據
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); //查詢2010-06-10到2010-07-31入職的員工
List users = session.createQuery("select u.id, u.name from User u where u.create_time between ? and ?")
.setParameter(0, sdf.parse("2010-06-10 00:00:00")) .setParameter(1, sdf.parse("2010-07-31 23:59:59")) .list();
說明:在實際項目開發中,關於日期段的查詢一般都是從起始日期的00:00:00時間開始,結束於結束日期的23:59:59,這樣對於日期類型的數據庫表的字段更爲精確。 
【小結】
可以採用拼字符串的方式傳遞參數
可以採用 ?來傳遞參數(索引從0開始) 可以採用 :參數名 來傳遞參數
如果傳遞多個參數,可以採用setParamterList方法
在HQL語言中可以使用數據庫的函數,如:CONVERT、DATEPART 
HQL語言-原生SQL語句查詢: 採用原生SQL語句查詢
List users = session.createSQLQuery("select * from t_user").list(); 說明:
使用原生SQL語句,需要使用:SQLQuery接口; 使用原生SQL語句時,不會往Session(一級)緩存中放入數據,即SQLQuery接口中的list()方法不會往Session(一級)緩存中放入數據;
SQLQuery接口的list()方法返回的List集合中存儲的是數組對象 
HQL語言-外置命名查詢:
    外置命名查詢就是指把HQL語句放到一個配置文件中,而不是把HQL語句直接寫到程序代碼中,採用這種方式的目的是爲了讓HQL語句與程序代碼更好的進行解耦。
在實際開發中這並不是決對的,因爲當把所有的HQL語句放入到一個配置文件中在調試時,會增加一定的工作量,有時顯得會不太方便,當項目追求低耦合度時,可以考慮採用這種方式。 
實體映射文件:
在</class>的標籤後面加入如下代碼: <!-- 外置命名查詢定義 --> <query name="searchUsers">
    <![CDATA[
                SELECT u FROM User u where u.id<?      ]]> </query>
說明: 配置文件中之所以使用<![CDATA[„ ]]>是因爲在HQL語句中含有”<”號,該符合是XML語言的組成部分,如果文本中含有XML語言特定的符號時可以將文本放在<![CDATA[„ ]]> 中,如果不使用<![CDATA[„ ]]>,則這些特定符號需要進行轉義,否則會引起一些問題。 
使用:
List users = session.getNamedQuery("searchUsers")
.setParameter(0, 6) .list(); 說明:
    searchUsers:爲配置文件中外置命名查詢的名稱
    setParameter(0, 6):爲外置命名查詢的HQL語句的佔位符賦值 在映射文件中採用<query>標籤來定義HQL語句
在程序中採用session.getNamedQuery()方法得到HQL查詢串 
HQL語言-查詢過濾器:
啓用查詢過濾器後,當Session沒有關閉的時候,只要在Session中執行的所有HQL語句都會自動加上查詢過濾器中的條件(即實現過濾功能)。 
(1)定義查詢過濾器
在</class>的標籤後面加入如下代碼:  <filter-def name="filtertest">
  <filter-param name="myid" type="integer"/>  </filter-def> 說明: 
filtertest:查詢過濾器名稱 myid:過濾條件中的參數名稱
type:指定參數的類型,本例爲小寫的integer表示這是Hibernate中的類型,也可以寫成Java中的java.lang.Integer 
(2)爲實體映射文件指定使用查詢過濾器 在<class>標籤中的最後加入如下代碼:
    <!-- 指定使用查詢過濾器,i_id數據庫字段名 -->
    <filter name="filtertest" condition="i_id &lt; :myid"/> 說明:
name:指定要使用的查詢過濾器名稱 condition:指定查詢過濾器的條件,將來過濾器將使用該條件進行過濾操作。其中“&lt; ”表示的是“<”號,因爲該符號是XML語言的特殊符號,所以在這裏需要進行轉義。 
(3)在程序中啓動查詢過濾器
//啓用查詢過濾器,只有啓動以後查詢過濾器才起作用 session.enableFilter("filtertest")     .setParameter("myid", 6); 【小結】
在映射文件中定義過濾器參數 在類的映射中使用這些參數 在程序中啓用過濾器
查詢過濾器中的條件在Hibernate生成具體的SQL語句時會自動加上該條件,所以條件中必須使用表中的字段名 
HQL語言-分頁查詢: Hibernate分頁查詢
List users = session.createQuery("from User")
//指定從第幾條記錄開始,下標從0開始,本例爲從第2條記錄開始查詢
.setFirstResult(1) //每頁顯示多少條記錄
.setMaxResults(2) .list(); 
HQL語言-對象導航查詢:
    所謂的對象導航查詢是指查詢某一個實體時,條件是這個實體所關聯的另外一個實體對象,例如:查詢員工的姓名,條件是根據部門名稱進行查詢。 對象導航查詢: List users = 
session.createQuery("select u.name from User u where u.dept.name like '%人力%'").list();
HQL語言的含義是:查找部門名稱中含有“人力”字樣的部門中的員工姓名字段。 說明:查詢的條件是根據User類中的關聯的實體對象dept的名稱來進行查詢的,其中dept是User類中對Dept的引用,那麼這種導航的工作原理是根據配置文件中的關聯映射完成的。 在HQL語句中採用 . 進行導航 
HQL語言-連接查詢:
Hibernate中支持3種連接查詢: 內連接 左外連接 右外連接
說明: HQL中的連接必須採用對象導航進行連接,否則將拋出 org.hibernate.hql.ast.QuerySyntaxException異常。

(1)內連接

使用inner關鍵字

List users = session.createQuery("select d.name, u.name from User u inner join u.dept d").list();

說明:  在Hibernate中上述 的HQL語句就能夠將User實體對象和Dept實體對象的關聯關係建立起來了,因爲這都是通過實體映射文件來完成的,體現在表上則是通過兩張表的連接完成的。

(2)左外連接

示例:查詢部門的名稱(包含沒有員工的部門)及員工的姓名
List users = session.createQuery("select d.name, u.name from Dept d left join d.users u").list();

說明:  Hibernate會根據Dept實體類的映射文件中的關聯映射關係自動發出左外連接的SQL語句(即t_dept表與t_user表之間採用左外連接的SQL語句)。

(3)右外連接

示例:查詢員工姓名(包含未分配部門的員工)及員工所在部門的名稱。
List users = session.createQuery("select d.name, u.name from Dept d right join d.users u").list(); 說明:Hibernate會根據Dept實體類的映射文件中的關聯映射關係自動發出右外連接的SQL語句(即t_dept表與t_user表之間採用右外連接的SQL語句)。 
HQL語言-統計查詢:
    Hibernate 3.x支持統計查詢,但是使用Hibernate做純粹的統計應用比較少,因爲涉及到的數據量非常大,在實際項目開發中,一般採用第三方的報表工具,比較常用的有:iReport、如意報表、JfreeChart等。 
示例:查詢員工人數
統計總數,一般有編程兩種方式:
(1)採用獲取返回的List集合長度的方式
         List users = session.createQuery("select count(id) from User").list();           //Hibernate的統計返回值一般都是long型,防止統計數據過大造成越界異常           Long count = (Long)users.get(0);
說明:這種方式效率不高,因爲返回的是一個List,而該List中就一個元素(即統計的數量值),然後還需要使用get方法將該元素取出。 (2)採用從返回的Long對象中獲取統計數值 Long count = 
     (Long)session.createQuery("select count(id) from User").uniqueResult(); 說明:這種方式效率高,因爲返回的不是一個List集合,而是一個長整型的對象,該對象即是統計出來的數據,因爲Hibernate使用長整型來封裝統計出來的數值,目的是防止統計出來的數值過大,造成數據溢出異常。數據類型一定要切記,是Long(即封裝類)。 
HQL語言-DML查詢:     Hibernate 3.x支持DML風格的操作,所謂DML風格就是指批量的執行insert、update、delete等操作這裏的這些操作指的都是批量執行Hibernate中對應的save、update、delete方法。
    但是這些批量操作一般不建議使用,因爲當批量執行一批語句後,Hibernate是不會修改緩存的,這就造成了數據庫中的數據與緩存中的數據不同步的問題,假設一次性批量插入1萬條數據,這時Hibernate不可能將這1萬條數據都放入緩存,因爲這非常容易導致內存溢出異常。
    在實際項目開發中對於那些變化不大數據( 即不是經常訪問的數據)可以採用DML來操作。 示例:
session.createQuery("update User u set u.name=? where u.id < ?")
.setParameter(0, "XXX") .setParameter(1, 5) .executeUpdate();
說明:執行完批量的update操作後數據庫中的值已經發生了變化,但是緩存中的值並沒有發生變化還是原來的值,但是當我們訪問時,其實訪問的是緩存中的值,這就造成了數據的不準確性。 
注:
session.get()和session.load()區別: get()方法:
1. 不支持延遲加載,會馬上發出查詢sql語句,加載User對象;
2. 使用get()方法加載數據,如果數據庫中不存在相應的數據,則返回null。 load()方法:
1. 支持延遲加載,只有真正使用這個對象的時候,纔會發出查詢sql語句
2. 使用load加載數據,如果數據庫中沒有相應的數據,那麼拋出ObjectNotFoundException,即不會返回null。
get(),load()方法會把查詢上來的數據放入緩存,也會使用緩存
get(),load()方法會將查詢上來的數據放入一級緩存中一份,然後再將該數據放入二級緩存中一份;先查找一級緩存再查找二級緩存,如果在任何一級緩存中能夠查到該數據,則直接進行使用,否則發出SQL加載數據
結論:session是共享二級緩存(SessionFactory緩存)的 
Query query = session.createQuery("") query.iterate()
iterate() 查詢實體對象: iterate() 使用緩存(但不包括查詢緩存,oid是存儲在查詢緩存中的),發出查詢id的SQL語句,不會發出查詢實體對象的SQL語句。
原理同load()方法,緩存中如果存在該實體對象,則直接使用,如果不存在該實體對象,則發出SQL語句去查詢。 
iterate() 方法查詢普通屬性:
一級緩存、二級緩存只存儲實體對象
發出查詢姓名屬性的SQL語句;不會發出查詢ID屬性列表的SQL語句(因爲此時的HQL語句查詢的是普通屬性而不是實體對象)
* 一(二)級緩存是緩存實體對象的,即如果查詢出來的是實體對象,則才向一(二)級緩存中存放,如果查詢出來的是普通屬性,則不向一(二)級緩存中存放。
重點!!!
* 思考:執行如下語句,是否將查詢出來的數據存放到一級緩存中?
 Iterator iter = session.createQuery("select new User(u.name) from User u where u.id=1").iterate();
  答案:不會向一級緩存中存放,因爲此時查詢出來的User對象是我們自己new出來的,而不是Hibernate自動爲我們new出來的,所以不受Session管理。 
Query.list()方法:
1、當關閉查詢緩存(默認)時,list()方法不使用一級緩存和二級緩存中的數據
2、當開啓查詢緩存時,list()方法就會使用一級緩存和二級緩存中的數據,順序是先找一級緩存再找二級緩存 
Session 、SessionFactory:
clear() 方法清除緩存中的全部實體對象
evict() 方法可以指定要從緩存中清除哪個實體對象
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章