新聞中心
Hibernate Query Language查詢:

讓客戶滿意是我們工作的目標(biāo),不斷超越客戶的期望值來(lái)自于我們對(duì)這個(gè)行業(yè)的熱愛(ài)。我們立志把好的技術(shù)通過(guò)有效、簡(jiǎn)單的方式提供給客戶,將通過(guò)不懈努力成為客戶在信息化領(lǐng)域值得信任、有價(jià)值的長(zhǎng)期合作伙伴,公司提供的服務(wù)項(xiàng)目有:國(guó)際域名空間、虛擬空間、營(yíng)銷軟件、網(wǎng)站建設(shè)、定海網(wǎng)站維護(hù)、網(wǎng)站推廣。
Criteria查詢對(duì)查詢條件進(jìn)行了面向?qū)ο蠓庋b,符合編程人員的思維方式,不過(guò)HQL(Hibernate Query Language)查詢提供了更加豐富的和靈活的查詢特性,因此Hibernate將HQL查詢方式立為官方推薦的標(biāo)準(zhǔn)查詢方式,HQL查詢?cè)诤wCriteria查詢的所有功能的前提下,提供了類似標(biāo)準(zhǔn)SQL語(yǔ)句的查詢方式,同時(shí)也提供了更加面向?qū)ο蟮姆庋b。完整的HQL語(yǔ)句形勢(shì)如下:
- Select/update/delete…… from …… where …… group by …… having …… order by …… asc/desc
其中的update/delete為Hibernate3中所新添加的功能,可見(jiàn)HQL查詢非常類似于標(biāo)準(zhǔn)Hibernate Query Language查詢。由于HQL查詢?cè)谡麄€(gè)Hibernate實(shí)體操作體系中的核心地位,這一節(jié)我將專門圍繞HQL操作的具體技術(shù)細(xì)節(jié)進(jìn)行講解。
1、 實(shí)體查詢:
有關(guān)實(shí)體查詢技術(shù),其實(shí)我們?cè)谙惹耙呀?jīng)有多次涉及,比如下面的例子:
- String hql=”from User user ”;
- List list=session.CreateQuery(hql).list();
上面的代碼執(zhí)行結(jié)果是,查詢出User實(shí)體對(duì)象所對(duì)應(yīng)的所有數(shù)據(jù),而且將數(shù)據(jù)封裝成User實(shí)體對(duì)象,并且放入List中返回。這里需要注意的是,Hibernate的實(shí)體查詢存在著對(duì)繼承關(guān)系的判定,比如我們前面討論映射實(shí)體繼承關(guān)系中的Employee實(shí)體對(duì)象,它有兩個(gè)子類分別是HourlyEmployee,SalariedEmployee,如果有這樣的HQL語(yǔ)句:“from Employee”,當(dāng)執(zhí)行檢索時(shí)Hibernate會(huì)檢索出所有Employee類型實(shí)體對(duì)象所對(duì)應(yīng)的數(shù)據(jù)(包括它的子類HourlyEmployee,SalariedEmployee對(duì)應(yīng)的數(shù)據(jù))。
因?yàn)镠ibernate Query Language語(yǔ)句與標(biāo)準(zhǔn)SQL語(yǔ)句相似,所以我們也可以在HQL語(yǔ)句中使用where字句,并且可以在where字句中使用各種表達(dá)式,比較操作符以及使用“and”,”or”連接不同的查詢條件的組合??聪旅娴囊恍┖?jiǎn)單的例子:
- 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%’;
2、 實(shí)體的更新和刪除:
在繼續(xù)講解Hibernate Query Language其他更為強(qiáng)大的查詢功能前,我們先來(lái)講解以下利用HQL進(jìn)行實(shí)體更新和刪除的技術(shù)。這項(xiàng)技術(shù)功能是Hibernate3的新加入的功能,在Hibernate2中是不具備的。比如在Hibernate2中,如果我們想將數(shù)據(jù)庫(kù)中所有18歲的用戶的年齡全部改為20歲,那么我們要首先將年齡在18歲的用戶檢索出來(lái),然后將他們的年齡修改為20歲,最后調(diào)用Session.update()語(yǔ)句進(jìn)行更新。在Hibernate3中對(duì)這個(gè)問(wèn)題提供了更加靈活和更具效率的解決辦法,如下面的代碼:
- 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();
通過(guò)這種方式我們可以在Hibernate3中,一次性完成批量數(shù)據(jù)的更新,對(duì)性能的提高是相當(dāng)?shù)目捎^。同樣也可以通過(guò)類似的方式來(lái)完成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();
如果你是逐個(gè)章節(jié)閱讀的化,那么你一定會(huì)記起我在第二部分中有關(guān)批量數(shù)據(jù)操作的相關(guān)論述中,討論過(guò)這種操作方式,這種操作方式在Hibernate3中稱為bulk delete/update,這種方式能夠在很大程度上提高操作的靈活性和運(yùn)行效率,但是采用這種方式極有可能引起緩存同步上的問(wèn)題(請(qǐng)參考相關(guān)論述)。
3、 屬性查詢:
很多時(shí)候我們?cè)跈z索數(shù)據(jù)時(shí),并不需要獲得實(shí)體對(duì)象所對(duì)應(yīng)的全部數(shù)據(jù),而只需要檢索實(shí)體對(duì)象的部分屬性所對(duì)應(yīng)的數(shù)據(jù)。這時(shí)候就可以利用HQL屬性查詢技術(shù),如下面程序示例:
- List list=session.createQuery(“select user.name from User user ”).list();
- for(int i=0;i
();i++){ - System.out.println(list.get(i));
- }
我們只檢索了User實(shí)體的name屬性對(duì)應(yīng)的數(shù)據(jù),此時(shí)返回的包含結(jié)果集的list中每個(gè)條目都是String類型的name屬性對(duì)應(yīng)的數(shù)據(jù)。我們也可以一次檢索多個(gè)屬性,如下面程序:
- List list=session.createQuery(“select user.name,user.age from User user ”).list();
- for(int i=0;i
();i++){ - Object[] obj=(Object[])list.get(i);
- System.out.println(obj[0]);
- System.out.println(obj[1]);
- }
此時(shí)返回的結(jié)果集list中,所包含的每個(gè)條目都是一個(gè)Object[]類型,其中包含對(duì)應(yīng)的屬性數(shù)據(jù)值。作為當(dāng)今我們這一代深受面向?qū)ο笏枷胗绊懙拈_(kāi)發(fā)人員,可能會(huì)覺(jué)得上面返回Object[]不夠符合面向?qū)ο箫L(fēng)格,這時(shí)我們可以利用HQL提供的動(dòng)態(tài)構(gòu)造實(shí)例的功能對(duì)這些平面數(shù)據(jù)進(jìn)行封裝,如下面的程序代碼:
- List list=session.createQuery(“select new User(user.name,user.age) from User user ”).list();
- for(int i=0;i
- User user=(User)list.get(i);
- System.out.println(user.getName());
- System.out.println(user.getAge());
- }
這里我們通過(guò)動(dòng)態(tài)構(gòu)造實(shí)例對(duì)象,對(duì)返回結(jié)果進(jìn)行了封裝,使我們的程序更加符合面向?qū)ο箫L(fēng)格,但是這里有一個(gè)問(wèn)題必須注意,那就是這時(shí)所返回的User對(duì)象,僅僅只是一個(gè)普通的Java對(duì)象而以,除了查詢結(jié)果值之外,其它的屬性值都為null(包括主鍵值id),也就是說(shuō)不能通過(guò)Session對(duì)象對(duì)此對(duì)象執(zhí)行持久化的更新操作。如下面的代碼:
- List list=session.createQuery(“select new User(user.name,user.age) from User user ”).list();
- for(int i=0;i
- User user=(User)list.get(i);
- user.setName(“gam”);
- session.saveOrUpdate(user);//
這里將會(huì)實(shí)際執(zhí)行一個(gè)save操作,而不會(huì)執(zhí)行update操作,因?yàn)檫@個(gè)User對(duì)象的id屬性為null,Hibernate會(huì)把它作為一個(gè)自由對(duì)象(請(qǐng)參考持久化對(duì)象狀態(tài)部分的論述),因此會(huì)對(duì)它執(zhí)行save操作。
}
4、 分組與排序
A、Order by子句:
與SQL語(yǔ)句相似,HQL查詢也可以通過(guò)order by子句對(duì)查詢結(jié)果集進(jìn)行排序,并且可以通過(guò)asc或者desc關(guān)鍵字指定排序方式,如下面的代碼:
- from User user order by user.name asc,user.age desc;
上面HQL查詢語(yǔ)句,會(huì)以name屬性進(jìn)行升序排序,以age屬性進(jìn)行降序排序,而且與SQL語(yǔ)句一樣,默認(rèn)的排序方式為asc,即升序排序。
B、Group by子句與統(tǒng)計(jì)查詢:
在HQL語(yǔ)句中同樣支持使用group by子句分組查詢,還支持group by子句結(jié)合聚集函數(shù)的分組統(tǒng)計(jì)查詢,大部分標(biāo)準(zhǔn)的SQL聚集函數(shù)都可以在HQL語(yǔ)句中使用,比如:count(),sum(),max(),min(),avg()等。如下面的程序代碼:
- String hql=”select count(user),user.age from User user group by user.age having count(user)>10 ”;
- List list=session.createQuery(hql).list();
C、優(yōu)化統(tǒng)計(jì)查詢:
假設(shè)我們現(xiàn)在有兩張數(shù)據(jù)庫(kù)表,分別是customer表和order表,它們的結(jié)構(gòu)如下:
- customer
- ID varchar2(14)
- age number(10)
- name varchar2(20)
- order
- ID varchar2(14)
- order_number number(10)
- customer_ID varchar2(14)
現(xiàn)在有兩條HQL查詢語(yǔ)句,分別如下:
- 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)
這兩條語(yǔ)句使用了HQL語(yǔ)句的內(nèi)連接查詢(我們將在HQL語(yǔ)句的連接查詢部分專門討論),現(xiàn)在我們可以看出這兩條查詢語(yǔ)句最后所返回的結(jié)果是一樣的,但是它們其實(shí)是有明顯區(qū)別的,語(yǔ)句(1)檢索的結(jié)果會(huì)返回Customer與Order持久化對(duì)象,而且它們會(huì)被置于Hibernate的Session緩存之中,并且Session會(huì)負(fù)責(zé)它們?cè)诰彺嬷械奈ㄒ恍砸约芭c后臺(tái)數(shù)據(jù)庫(kù)數(shù)據(jù)的同步,只有事務(wù)提交后它們才會(huì)從緩存中被清除;而語(yǔ)句(2)返回的是關(guān)系數(shù)據(jù)而并非是持久化對(duì)象,因此它們不會(huì)占用Hibernate的Session緩存,只要在檢索之后應(yīng)用程序不在訪問(wèn)它們,它們所占用的內(nèi)存就有可能被JVM的垃圾回收器回收,而且Hibernate不會(huì)同步對(duì)它們的修改。
在我們的系統(tǒng)開(kāi)發(fā)中,尤其是Mis系統(tǒng),不可避免的要進(jìn)行統(tǒng)計(jì)查詢的開(kāi)發(fā),這類功能有兩個(gè)特點(diǎn):第一數(shù)據(jù)量大;第二一般情況下都是只讀操作而不會(huì)涉及到對(duì)統(tǒng)計(jì)數(shù)據(jù)進(jìn)行修改,那么如果采用第一種查詢方式,必然會(huì)導(dǎo)致大量持久化對(duì)象位于Hibernate的Session緩存中,而且Hibernate的Session緩存還要負(fù)責(zé)它們與數(shù)據(jù)庫(kù)數(shù)據(jù)的同步。而如果采用第二種查詢方式,顯然就會(huì)提高查詢性能,因?yàn)椴恍枰狧ibernate的Session緩存的管理開(kāi)銷,而且只要應(yīng)用程序不在使用這些數(shù)據(jù),它們所占用的內(nèi)存空間就會(huì)被回收釋放。
因此在開(kāi)發(fā)統(tǒng)計(jì)查詢系統(tǒng)時(shí),盡量使用通過(guò)select語(yǔ)句寫(xiě)出需要查詢的屬性的方式來(lái)返回關(guān)系數(shù)據(jù),而避免使用第一種查詢方式返回持久化對(duì)象(這種方式是在有修改需求時(shí)使用比較適合),這樣可以提高運(yùn)行效率并且減少內(nèi)存消耗。真正的高手并不是精通一切,而是精通在合適的場(chǎng)合使用合適的手段。
5、 參數(shù)綁定:
Hibernate中對(duì)動(dòng)態(tài)查詢參數(shù)綁定提供了豐富的支持,那么什么是查詢參數(shù)動(dòng)態(tài)綁定呢?其實(shí)如果我們熟悉傳統(tǒng)JDBC編程的話,我們就不難理解查詢參數(shù)動(dòng)態(tài)綁定,如下代碼傳統(tǒng)JDBC的參數(shù)綁定:
- PrepareStatement pre=connection.prepare(“select * from User where user.name=?”);
- pre.setString(1,”zhaoxin”);
- ResultSet rs=pre.executeQuery();
在Hibernate中也提供了類似這種的查詢參數(shù)綁定功能,而且在Hibernate中對(duì)這個(gè)功能還提供了比傳統(tǒng)JDBC操作豐富的多的特性,在Hibernate中共存在4種參數(shù)綁定的方式,下面我們將分別介紹:
A、 按參數(shù)名稱綁定:
在HQL語(yǔ)句中定義命名參數(shù)要用”:”開(kāi)頭,形式如下:
- Query query=session.createQuery(“from User user where user.name=:customername and user:customerage=:age ”);
- query.setString(“customername”,name);
- query.setInteger(“customerage”,age);
上面代碼中用:customername和:customerage分別定義了命名參數(shù)customername和customerage,然后用Query接口的setXXX()方法設(shè)定名參數(shù)值,setXXX()方法包含兩個(gè)參數(shù),分別是命名參數(shù)名稱和命名參數(shù)實(shí)際值。
B、 按參數(shù)位置邦定:
在HQL查詢語(yǔ)句中用”?”來(lái)定義參數(shù)位置,形式如下:
- Query query=session.createQuery(“from User user where user.name=? and user.age =? ”);
- query.setString(0,name);
- query.setInteger(1,age);
同樣使用setXXX()方法設(shè)定綁定參數(shù),只不過(guò)這時(shí)setXXX()方法的第一個(gè)參數(shù)代表邦定參數(shù)在HQL語(yǔ)句中出現(xiàn)的位置編號(hào)(由0開(kāi)始編號(hào)),第二個(gè)參數(shù)仍然代表參數(shù)實(shí)際值。
注:在實(shí)際開(kāi)發(fā)中,提倡使用按名稱邦定命名參數(shù),因?yàn)檫@不但可以提供非常好的程序可讀性,而且也提高了程序的易維護(hù)性,因?yàn)楫?dāng)查詢參數(shù)的位置發(fā)生改變時(shí),按名稱邦定名參數(shù)的方式中是不需要調(diào)整程序代碼的。
C、 setParameter()方法:
在Hibernate的HQL查詢中可以通過(guò)setParameter()方法邦定任意類型的參數(shù),如下代碼:
- String hql=”from User user where user.name=:customername ”;
- Query query=session.createQuery(hql);
- query.setParameter(“customername”,name,Hibernate.STRING);
如上面代碼所示,setParameter()方法包含三個(gè)參數(shù),分別是命名參數(shù)名稱,命名參數(shù)實(shí)際值,以及命名參數(shù)映射類型。對(duì)于某些參數(shù)類型setParameter()方法可以根據(jù)參數(shù)值的Java類型,猜測(cè)出對(duì)應(yīng)的映射類型,因此這時(shí)不需要顯示寫(xiě)出映射類型,像上面的例子,可以直接這樣寫(xiě):
query.setParameter(“customername”,name);但是對(duì)于一些類型就必須寫(xiě)明映射類型,比如java.util.Date類型,因?yàn)樗鼤?huì)對(duì)應(yīng)Hibernate的多種映射類型,比如Hibernate.DATA或者Hibernate.TIMESTAMP。
D、 setProperties()方法:
在Hibernate中可以使用setProperties()方法,將命名參數(shù)與一個(gè)對(duì)象的屬性值綁定在一起,如下程序代碼:
- 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()方法會(huì)自動(dòng)將customer對(duì)象實(shí)例的屬性值匹配到命名參數(shù)上,但是要求命名參數(shù)名稱必須要與實(shí)體對(duì)象相應(yīng)的屬性同名。
這里還有一個(gè)特殊的setEntity()方法,它會(huì)把命名參數(shù)與一個(gè)持久化對(duì)象相關(guān)聯(lián),如下面代碼所示:
- 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();
上面的代碼會(huì)生成類似如下的SQL語(yǔ)句:
- Select * from order where customer_ID=’1’;
E、 使用綁定參數(shù)的優(yōu)勢(shì):
我們?yōu)槭裁匆褂媒壎麉?shù)?任何一個(gè)事物的存在都是有其價(jià)值的,具體到綁定參數(shù)對(duì)于HQL查詢來(lái)說(shuō),主要有以下兩個(gè)主要優(yōu)勢(shì):
①、 可以利用數(shù)據(jù)庫(kù)實(shí)施性能優(yōu)化,因?yàn)閷?duì)Hibernate來(lái)說(shuō)在底層使用的是PrepareStatement來(lái)完成查詢,因此對(duì)于語(yǔ)法相同參數(shù)不同的SQL語(yǔ)句,可以充分利用預(yù)編譯SQL語(yǔ)句緩存,從而提升查詢效率。
②、 可以防止SQL Injection安全漏洞的產(chǎn)生:
SQL Injection是一種專門針對(duì)SQL語(yǔ)句拼裝的攻擊方式,比如對(duì)于我們常見(jiàn)的用戶登錄,在登錄界面上,用戶輸入用戶名和口令,這時(shí)登錄驗(yàn)證程序可能會(huì)生成如下的HQL語(yǔ)句:
- “from User user where user.name=’”+name+”’ and user.password=’”+password+”’ ”
這個(gè)HQL語(yǔ)句從邏輯上來(lái)說(shuō)是沒(méi)有任何問(wèn)題的,這個(gè)登錄驗(yàn)證功能在一般情況下也是會(huì)正確完成的,但是如果在登錄時(shí)在用戶名中輸入”zhaoxin or ‘x’=’x”,這時(shí)如果使用簡(jiǎn)單的HQL語(yǔ)句的字符串拼裝,就會(huì)生成如下的HQL語(yǔ)句:
- “from User user where user.name=’zhaoxin’ or ‘x’=’x’ and user.password=’admin’ ”;
顯然這條HQL語(yǔ)句的where字句將會(huì)永遠(yuǎn)為真,而使用戶口令的作用失去意義,這就是SQL Injection攻擊的基本原理。
而使用綁定參數(shù)方式,就可以妥善處理這問(wèn)題,當(dāng)使用綁定參數(shù)時(shí),會(huì)得到下面的HQL語(yǔ)句:
- from User user where user.name=’’zhaoxin’’ or ‘’x=’’x’’ ‘ and user.password=’admin’;
由此可見(jiàn)使用綁定參數(shù)會(huì)將用戶名中輸入的單引號(hào)解析成字符串(如果想在字符串中包含單引號(hào),應(yīng)使用重復(fù)單引號(hào)形式),所以參數(shù)綁定能夠有效防止SQL Injection安全漏洞。
【編輯推薦】
- 強(qiáng)人Hibernate文檔筆記(上)
- 強(qiáng)人Hibernate文檔筆記(中)
- 強(qiáng)人Hibernate文檔筆記(下)
- Hibernate一對(duì)多關(guān)系的處理
- Hibernate的性能優(yōu)化
文章名稱:Hibernate Query Language查詢
URL標(biāo)題:http://m.fisionsoft.com.cn/article/cdhhooh.html


咨詢
建站咨詢
