Hibernate繼續進階之hql語句和QBC查詢

Hibernate--hql語句查詢

HQL查詢:

Criteria查詢對查詢條件進行了面向對象封裝,符合編程人員的思維方式,不過HQL(Hibernate Query Lanaguage)查詢提供了更加豐富的和靈活的查詢特性,因此Hibernate將HQL查詢方式立為官方推薦的標準查詢方式,HQL查詢在涵蓋Criteria查詢的所有功能的前提下,提供了類似標準SQL語句的查詢方式,同時也提供了更加面向對象的封裝。完整的HQL語句形勢如下:

Select/update/delete…… from …… where …… group by …… having …… order by …… asc/desc

其中的update/delete為Hibernate3中所新添加的功能,可見HQL查詢非常類似于標準SQL查詢。由于HQL查詢在整個Hibernate實體操作體系中的核心地位

實體查詢:

有關實體查詢技術,其實我們在先前已經有多次涉及,比如下面的例子:

String hql=”from User user ”;
List<User> list=session.CreateQuery(hql).list();

上面的代碼執行結果是,查詢出User實體對象所對應的所有數據,而且將數據封裝成User實體對象,并且放入List中返回。這里需要注意的是,Hibernate的實體查詢存在著對繼承關系的判定,比如我們前面討論映射實體繼承關系中的Employee實體對象,它有兩個子類分別是HourlyEmployee,SalariedEmployee,如果有這樣的HQL語句:“from Employee”,當執行檢索時Hibernate會檢索出所有Employee類型實體對象所對應的數據(包括它的子類HourlyEmployee,SalariedEmployee對應的數據)。

因為HQL語句與標準SQL語句相似,所以我們也可以在HQL語句中使用where字句,并且可以在where字句中使用各種表達式,比較操作符以及使用“and”,”or”連接不同的查詢條件的組合。看下面的一些簡單的例子:

from User user where user.age=20;
from User user where user.age between 20 and 30;
from User user where user.age in(20,30);
from User user where user.name is null;
from User user where user.name like ‘%zx%’;
from User user where (user.age%2)=1;
from User user where user.age=20 and user.name like ‘%zx%’;

實體的更新和刪除:

在繼續講解HQL其他更為強大的查詢功能前,我們先來講解以下利用HQL進行實體更新和刪除的技術。這項技術功能是Hibernate3的新加入的功能,在Hibernate2中是不具備的。比如在Hibernate2中,如果我們想將數據庫中所有18歲的用戶的年齡全部改為20歲,那么我們要首先將年齡在18歲的用戶檢索出來,然后將他們的年齡修改為20歲,最后調用Session.update()語句進行更新。在Hibernate3中對這個問題提供了更加靈活和更具效率的解決辦法,如下面的代碼:

Transaction trans=session.beginTransaction();
String hql=”update User user set user.age=20 where user.age=18”;
Query queryupdate=session.createQuery(hql);
int ret=queryupdate.executeUpdate();
trans.commit();

通過這種方式我們可以在Hibernate3中,一次性完成批量數據的更新,對性能的提高是相當的可觀。同樣也可以通過類似的方式來完成delete操作,如下面的代碼:

Transaction trans=session.beginTransaction();
String hql=”delete from User user where user.age=18”;
Query queryupdate=session.createQuery(hql);
int ret=queryupdate.executeUpdate();
trans.commit();

屬性查詢:

很多時候我們在檢索數據時,并不需要獲得實體對象所對應的全部數據,而只需要檢索實體對象的部分屬性所對應的數據。這時候就可以利用HQL屬性查詢技術,如下面程序示例:

List list=session.createQuery(“select user.name from User user ”).list();
for(int i=0;i<list.size();i++){
 System.out.println(list.get(i));
}

我們只檢索了User實體的name屬性對應的數據,此時返回的包含結果集的list中每個條目都是String類型的name屬性對應的數據。我們也可以一次檢索多個屬性,如下面程序:

List<Object[]> list=session.createQuery(“select user.name,user.age from User user ”).list();
for(int i=0;i<list.size();i++){
 Object[] obj=(Object[])list.get(i);
 System.out.println(obj[0]);
 System.out.println(obj[1]);
}

此時返回的結果集list中,所包含的每個條目都是一個Object[]類型,其中包含對應的屬性數據值。作為當今我們這一代深受面向對象思想影響的開發人員,可能會覺得上面返回Object[]不夠符合面向對象風格,這時我們可以利用HQL提供的動態構造實例的功能對這些平面數據進行封裝,如下面的程序代碼:

List list=session.createQuery(“select new User(user.name,user.age) from User user ”).list();
for(int i=0;i<list.size();i++){
 User user=(User)list.get(i);
 System.out.println(user.getName());
 System.out.println(user.getAge());
}

這里我們通過動態構造實例對象,對返回結果進行了封裝,使我們的程序更加符合面向對象風格,但是這里有一個問題必須注意,那就是這時所返回的User對象,僅僅只是一個普通的Java對象而以,除了查詢結果值之外,其它的屬性值都為null(包括主鍵值id),也就是說不能通過Session對象對此對象執行持久化的更新操作。如下面的代碼:

List list=session.createQuery(“select new User(user.name,user.age) from User user ”).list();
for(int i=0;i<list.size();i++){
 User user=(User)list.get(i);
 user.setName(“gam”);
 session.saveOrUpdate(user);//這里將會實際執行一個save操作,而不會執行update操作,因為這個User對象的id屬性為null,Hibernate會把它作為一個自由對象(請參考持久化對象狀態部分的論述),因此會對它執行save操作。
}

分組與排序

Order by子句:

與SQL語句相似,HQL查詢也可以通過order by子句對查詢結果集進行排序,并且可以通過asc或者desc關鍵字指定排序方式,如下面的代碼:

from User user order by user.name asc,user.age desc;

上面HQL查詢語句,會以name屬性進行升序排序,以age屬性進行降序排序,而且與SQL語句一樣,默認的排序方式為asc,即升序排序。

Group by子句與統計查詢:

在HQL語句中同樣支持使用group by子句分組查詢,還支持group by子句結合聚集函數的分組統計查詢,大部分標準的SQL聚集函數都可以在HQL語句中使用,比如:count(),sum(),max(),min(),avg()等。如下面的程序代碼:

//對User表按年齡分組查詢那個年齡人數大于十個的年齡和數量。   
String hql=”select count(user),user.age from User user group by user.age having count(user)>10 ”;
List list=session.createQuery(hql).list();
優化統計查詢:

假設我們現在有兩張數據庫表,分別是customer表和order表,它們的結構如下:

customer
ID varchar2(14)
age number(10)
name varchar2(20)
 
order
ID varchar2(14)
order_number number(10)
customer_ID varchar2(14)

現在有兩條HQL查詢語句,分別如下:

  • from Customer c inner join c.orders o group by c.age;(1)

  • select c.ID,c.name,c.age,o.ID,o.order_number,o.customer_ID
    from Customer c inner join c.orders c group by c.age;(2)

這兩條語句使用了HQL語句的內連接查詢(我們將在HQL語句的連接查詢部分專門討論),現在我們可以看出這兩條查詢語句最后所返回的結果是一樣的,但是它們其實是有明顯區別的,

  • 語句(1)檢索的結果會返回Customer與Order持久化對象,而且它們會被置于Hibernate的Session緩存之中,并且Session會負責它們在緩存中的唯一性以及與后臺數據庫數據的同步,只有事務提交后它們才會從緩存中被清除;
  • 而語句(2)返回的是關系數據而并非是持久化對象,因此它們不會占用Hibernate的Session緩存,只要在檢索之后應用程序不在訪問它們,它們所占用的內存就有可能被JVM的垃圾回收器回收,而且Hibernate不會同步對它們的修改。

在我們的系統開發中,尤其是Mis系統,不可避免的要進行統計查詢的開發,這類功能有兩個特點:第一數據量大;第二一般情況下都是只讀操作而不會涉及到對統計數據進行修改,那么如果采用第一種查詢方式,必然會導致大量持久化對象位于Hibernate的Session緩存中,而且Hibernate的Session緩存還要負責它們與數據庫數據的同步。而如果采用第二種查詢方式,顯然就會提高查詢性能,因為不需要Hibernate的Session緩存的管理開銷,而且只要應用程序不在使用這些數據,它們所占用的內存空間就會被回收釋放。
因此在開發統計查詢系統時,盡量使用通過select語句寫出需要查詢的屬性的方式來返回關系數據,而避免使用第一種查詢方式返回持久化對象(這種方式是在有修改需求時使用比較適合),這樣可以提高運行效率并且減少內存消耗。

真正的高手并不是精通一切,而是精通在合適的場合使用合適的手段。

參數綁定:

Hibernate中對動態查詢參數綁定提供了豐富的支持,那么什么是查詢參數動態綁定呢?其實如果我們熟悉傳統JDBC編程的話,我們就不難理解查詢參數動態綁定,如下代碼傳統JDBC的參數綁定:

PrepareStatement pre=connection.prepare(“select * from User where user.name=?”);
pre.setString(1,”zhaoxin”);
ResultSet rs=pre.executeQuery();

在Hibernate中也提供了類似這種的查詢參數綁定功能,而且在Hibernate中對這個功能還提供了比傳統JDBC操作豐富的多的特性,在Hibernate中共存在4種參數綁定的方式,下面我們將分別介紹:

按參數名稱綁定:

在HQL語句中定義命名參數要用”:”開頭,形式如下:

 Query query=session.createQuery(“from User user where user.name=:customername and user.age=:customerage”);
 query.setString(“customername”,name);
 query.setInteger(“customerage”,age);

上面代碼中用:customername和:customerage分別定義了命名參數customername和customerage,然后用Query接口的setXXX()方法設定名參數值,setXXX()方法包含兩個參數,分別是命名參數名稱和命名參數實際值。

按參數位置邦定:

在HQL查詢語句中用”?”來定義參數位置,形式如下:

Query query=session.createQuery(“from User user where user.name=? and user.age =? ”);
 query.setString(0,name);
 query.setInteger(1,age);

同樣使用setXXX()方法設定綁定參數,只不過這時setXXX()方法的第一個參數代表邦定參數在HQL語句中出現的位置編號(由0開始編號),第二個參數仍然代表參數實際值。
注:在實際開發中,提倡使用按名稱邦定命名參數,因為這不但可以提供非常好的程序可讀性,而且也提高了程序的易維護性,因為當查詢參數的位置發生改變時,按名稱邦定名參數的方式中是不需要調整程序代碼的。

setParameter()方法:

在Hibernate的HQL查詢中可以通過setParameter()方法邦定任意類型的參數,如下代碼:

 String hql=”from User user where user.name=:customername”;
 Query query=session.createQuery(hql);
 query.setParameter(“customername”,name,Hibernate.STRING);

如上面代碼所示,setParameter()方法包含三個參數,分別是命名參數名稱,命名參數實際值,以及命名參數映射類型。對于某些參數類型setParameter()方法可以更具參數值的Java類型,猜測出對應的映射類型,因此這時不需要顯示寫出映射類型,像上面的例子,可以直接這樣寫:
query.setParameter(“customername”,name);但是對于一些類型就必須寫明映射類型,比如java.util.Date類型,因為它會對應Hibernate的多種映射類型,比如Hibernate.DATA或者Hibernate.TIMESTAMP。

setProperties()方法:

在Hibernate中可以使用setProperties()方法,將命名參數與一個對象的屬性值綁定在一起,如下程序代碼:

Customer customer=new Customer();
customer.setName(“pansl”);
customer.setAge(80);
Query query=session.createQuery(“from Customer c where c.name=:name and c.age=:age ”);
query.setProperties(customer);

setProperties()方法會自動將customer對象實例的屬性值匹配到命名參數上,但是要求命名參數名稱必須要與實體對象相應的屬性同名。
這里還有一個特殊的setEntity()方法,它會把命名參數與一個持久化對象相關聯,如下面代碼所示:

Customer customer=(Customer)session.load(Customer.class,”1”);
Query query=session.createQuery(“from Order order where order.customer=:customer ”);
query. setProperties(“customer”,customer);
List list=query.list();
上面的代碼會生成類似如下的SQL語句:
Select * from order where customer_ID=’1’;
使用綁定參數的優勢:

我們為什么要使用綁定命名參數?任何一個事物的存在都是有其價值的,具體到綁定參數對于HQL查詢來說,主要有以下兩個主要優勢:

  • 可以利用數據庫實施性能優化,因為對Hibernate來說在底層使用的是PrepareStatement來完成查詢,因此對于語法相同參數不同的SQL語句,可以充分利用預編譯SQL語句緩存,從而提升查詢效率。
  • 可以防止SQL Injection安全漏洞的產生:
    SQL Injection是一種專門針對SQL語句拼裝的攻擊方式,比如對于我們常見的用戶登錄,在登錄界面上,用戶輸入用戶名和口令,這時登錄驗證程序可能會生成如下的HQL語句:
    “from User user where user.name=’”+name+”’ and user.password=’”+password+”’ ”
    這個HQL語句從邏輯上來說是沒有任何問題的,這個登錄驗證功能在一般情況下也是會正確完成的,但是如果在登錄時在用戶名中輸入”zhaoxin or ‘x’=’x”,這時如果使用簡單的HQL語句的字符串拼裝,就會生成如下的HQL語句:
    “from User user where user.name=’zhaoxin’ or ‘x’=’x’ and user.password=’admin’ ”;
    顯然這條HQL語句的where字句將會永遠為真,而使用戶口令的作用失去意義,這就是SQL Injection攻擊的基本原理。
    而使用綁定參數方式,就可以妥善處理這問題,當使用綁定參數時,會得到下面的HQL語句:
    from User user where user.name=’’zhaoxin’’ or ‘’x=’’x’’ ‘ and user.password=’admin’;由此可見使用綁定參數會將用戶名中輸入的單引號解析成字符串(如果想在字符串中包含單引號,應使用重復單引號形式),所以參數綁定能夠有效防止SQL Injection安全漏洞。

QBC查詢

查詢所有

例4: 查詢出所有的顧客信息

    Criteria criteria = session.createCriteria(Customer.class);
    List<Customer> customers = criteria.list();
    for(Customer cus : customers){
        System.out.println(cus);
    }

Restrictions用法

Restrictions.eq    --> equal,等于.
Restrictions.allEq --> 參數為Map對象,使用key/value進行多個等于的比對,相當于多個Restrictions.eq的效果
Restrictions.gt    --> great-than > 大于
Restrictions.ge    --> great-equal >= 大于等于
Restrictions.lt    --> less-than, < 小于
Restrictions.le    --> less-equal <= 小于等于
Restrictions.between --> 對應SQL的between子句
Restrictions.like  --> 對應SQL的LIKE子句
Restrictions.in    --> 對應SQL的in子句
Restrictions.and   --> and 關系
Restrictions.or    --> or 關系
Restrictions.isNull--> 判斷屬性是否為空,為空則返回true
Restrictions.isNotNull --> 與isNull相反

例5: 查詢符合條件的顧客的信息(年齡大于12)

    Criteria criteria = session.createCriteria(Customer.class);
    criteria.add(Restrictions.gt("age", 12));
    List<Customer> customers = criteria.list();
    for(Customer cus : customers){
        System.out.println(cus);
    }

例6: 查詢名字在"terry,larry,tom"之間的用戶的信息

    Session session = HibernateSessionFactory.getSession();
    Transaction tran = session.beginTransaction();
    String[] names = {"terry","larry","tom"}; 
    Criteria criteria = session.createCriteria(Customer.class);
    criteria.add(Restrictions.in("name", names));
    List<Customer> customers = criteria.list();
    for(Customer cus : customers){
        System.out.println(cus);
    }       

例7: 年齡為空的用戶信息

    criteria.add(Restrictions.isNull("age"))

例8: 查詢名字在"terry,larry,tom"之間的用戶的信息或者年齡等于15的顧客的信息

    criteria.add(Restrictions.or(Restrictions.in("name", names),   
    Restrictions.eq("age", 15)));

Order 排序

criteria.addOrder() 添加排序約束
 Order.desc("id") 按照id降序排列
 Order.asc("id")  按照id升序排列

其他

Conjunction
Disjunction
Example
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,786評論 6 534
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,656評論 3 419
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,697評論 0 379
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,098評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,855評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,254評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,322評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,473評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,014評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,833評論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,016評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,568評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,273評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,680評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,946評論 1 288
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,730評論 3 393
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,006評論 2 374

推薦閱讀更多精彩內容

  • 1. Java基礎部分 基礎部分的順序:基本語法,類相關的語法,內部類的語法,繼承相關的語法,異常的語法,線程的語...
    子非魚_t_閱讀 31,722評論 18 399
  • 本文包括:1、Hibernate 的查詢方式2、HQL (Hibernate Query Language) 查詢...
    廖少少閱讀 2,681評論 0 15
  • Hibernate: 一個持久化框架 一個ORM框架 加載:根據特定的OID,把一個對象從數據庫加載到內存中OID...
    JHMichael閱讀 1,985評論 0 27
  • 一. Java基礎部分.................................................
    wy_sure閱讀 3,828評論 0 11
  • 圖片來源:Wiki 導語: 猴年到了,在這里給大家拜個猴年,并跟大家說說猴。作為一個“生物男”,當我們說猴的時候,...
    知識分子閱讀 1,124評論 0 0