HQL

HQL

HQL 是Hibernate Query Language的簡寫,即hibernate查詢語言:HQL採用面向對象的查詢方式。

基本信息

定義

Criteria查詢對查詢條件進行了面向對象封裝,符合編程人員的思維方式,不過HQL(HibernateQueryLanguage)查詢提供了更加豐富的和靈活的查詢特性,因此Hibernate將HQL查詢方式立為官方推薦的標準查詢方式,HQL查詢在涵蓋Criteria查詢的所有功能的前提下,提供了類似標準SQL語句的查詢方式,同時也提供了更加面向對象的封裝。完整的HQL語句形式如下:Select/update/delete……、from……、where……、groupby……、having……、orderby……其中的update/delete為Hibernate3中所新添加的功能,可見HQL查詢非常類似於標準SQL查詢。

用法

實體查詢

有關實體查詢技術,其實我們在先前已經有多次涉及,比如下面的例子:Stringhql=”fromUseruser”;Listlist=session.CreateQuery(hql).list();上面的代碼執行結果是,查詢出User實體對象所對應的所有數據,而且將數據封裝成User實體對象,並且放入List中返回。這裡需要注意的是,Hibernate的實體查詢存在著對繼承關係的判定,比如我們前面討論映射實體繼承關係中的Employee實體對象,它有兩個子類分別是HourlyEmployee,SalariedEmployee,如果有這樣的HQL語句:“fromEmployee”,當執行檢索時Hibernate會檢索出所有Employee類型實體對象所對應的數據(包括它的子類HourlyEmployee,SalariedEmployee對應的數據)。因為HQL語句與標準SQL語句相似,所以我們也可以在HQL語句中使用where字句,並且可以在where字句中使用各種表達式,比較操作符以及使用“and”,”or”連線不同的查詢條件的組合。看下面的一些簡單的例子:
fromUseruserwhereuserage=20;
fromUseruserwhereuseragebetween20and30;
fromUseruserwhereuseragein(20,30);
fromUseruserwhereusernameisnull;
fromUseruserwhereusernamelike‘%zx%’;
fromUseruserwhere(userage%2)=1;
fromUseruserwhereuserage=20andusernamelike‘%zx%’;

實體的更新和刪除

這項技術功能是Hibernate3的新加入的功能,在Hibernate2中是不具備的。比如在Hibernate2中,如果我們想將資料庫中所有18歲的用戶的年齡全部改為20歲,那么我們要首先將年齡在18歲的用戶檢索出來,然後將他們的年齡修改為20歲,最後調用Sessionupdate()語句進行更新。在Hibernate3中對這個問題提供了更加靈活和更具效率的解決辦法,如下面的代碼:
Transactiontrans=sessionbeginTransaction();
Stringhql=”updateUserusersetuserage=20whereuserage=18”;
Queryqueryupdate=sessioncreateQuery(hql);
intret=queryupdateexecuteUpdate();
transcommit;
通過這種方式我們可以在Hibernate3中,一次性完成批量數據的更新,對性能的提高是相當的可觀。同樣也可以通過類似的方式來完成delete操作,如下面的代碼:
Transactiontrans=sessionbeginTransaction();
Stringhql=”deletefromUseruserwhereuserage=18”;
Queryqueryupdate=sessioncreateQuery(hql);
intret=queryupdateexecuteUpdate();
transcommit();
如果你是逐個章節閱讀的話,那么你一定會記起我在第二部分中有關批量數據操作的相關論述中,討論過這種操作方式,這種操作方式在Hibernate3中稱為bulkdelete/update,這種方式能夠在很大程度上提高操作的靈活性和運行效率,但是採用這種方式極有可能引起快取同步上的問題(請參考相關論述)。

屬性查詢

很多時候我們在檢索數據時,並不需要獲得實體對象所對應的全部數據,而只需要檢索實體對象的部分屬性所對應的數據。這時候就可以利用HQL屬性查詢技術,如下面程式示例:
Listlist=sessioncreateQuery(“selectusernamefromUseruser”)
list();
for(inti=0;i<list.size();i++){
Systemoutprintln(listget(i));
}
我們只檢索了User實體的name屬性對應的數據,此時返回的包含結果集的list中每個條目都是String類型的name屬性對應的數據。我們也可以一次檢索多個屬性,如下面程式:
Listlist=sessioncreateQuery(“selectusername,useragefromUseruser”)
list();
for(inti=0;i<listsize();i++){
Object[]obj=(Object[])
listget(i);
Systemoutprintln(obj[0]);
Systemoutprintln(obj[1]);
}
此時返回的結果集list中,所包含的每個條目都是一個Object[]類型,其中包含對應的屬性數據值。作為當今我們這一代深受面向對象思想影響的開發人員,可能會覺得上面返回Object[]不夠符合面向對象風格,這時我們可以利用HQL提供的動態構造實例的功能對這些平面數據進行封裝,如下面的程式代碼:
Listlist=sessioncreateQuery(“selectnewUser(username,userage)fromUseruser”)
list();
for(inti=0;i<listsize();i++){
Useruser=(User)listget(i);
Systemoutprintln(usergetName());
Systemoutprintln(usergetAge());
}
這裡我們通過動態構造實例對象,對返回結果進行了封裝,使我們的程式更加符合面向對象風格,但是這裡有一個問題必須注意,那就是這時所返回的User對象,僅僅只是一個普通的Java對象而以,除了查詢結果值之外,其它的屬性值都為null(包括主鍵值id),也就是說不能通過Session對象對此對象執行持久化的更新操作。如下面的代碼:
Listlist=sessioncreateQuery(“selectnewUser(username,userage)fromUseruser”)
list();
for(inti=0;i<listsize();i++){
Useruser=(User)listget(i);
usersetName(“gam”);
sessionsaveOrUpdate(user);//這裡將會實際執行一個save操作,而不會執行update操作,因為這個User對象的id屬性為null,Hibernate會把它作為一個自由對象(請參考持久化對象狀態部分的論述),因此會對它執行save操作。
}

分組與排序

A、Orderby子句
與SQL語句相似,HQL查詢也可以通過orderby子句對查詢結果集進行排序,並且可以通過asc或者desc關鍵字指定排序方式,如下面的代碼:fromUseruserorderbyusernameasc,useragedesc;上面HQL查詢語句,會以name屬性進行升序排序,以age屬性進行降序排序,而且與SQL語句一樣,默認的排序方式為asc,即升序排序。
B、Groupby子句與統計查詢
在HQL語句中同樣支持使用groupby子句分組查詢,還支持groupby子句結合聚集函式的分組統計查詢,大部分標準的SQL聚集函式都可以在HQL語句中使用,比如:count(),sum(),max(),min(),avg()等。
C、最佳化統計查詢
假設我們現在有兩張資料庫表,分別是customer表和order表,它們的結構如下:
customerIDvarchar2(14)
agenumber(10)
namevarchar2(20)
orderIDvarchar2(14)
order_numbernumber(10)
customer_IDvarchar2(14)
在我們的系統開發中,尤其是Mis系統,不可避免的要進行統計查詢的開發,這類功能有兩個特點:第一數據量大;第二一般情況下都是唯讀操作而不會涉及到對統計數據進行修改,那么如果採用第一種查詢方式,必然會導致大量持久化對象位於Hibernate的Session快取中,而且Hibernate的Session快取還要負責它們與資料庫數據的同步。而如果採用第二種查詢方式,顯然就會提高查詢性能,因為不需要Hibernate的Session快取的管理開銷,而且只要應用程式不在使用這些數據,它們所占用的記憶體空間就會被回收釋放。因此在開發統計查詢系統時,儘量使用通過select語句寫出需要查詢的屬性的方式來返回關係數據,而避免使用第一種查詢方式返回持久化對象(這種方式是在有修改需求時使用比較適合),這樣可以提高運行效率並且減少記憶體消耗。㊣真正的高手並不是精通一切,而是精通在合適的場合使用合適的手段。

參數綁定

Hibernate中對動態查詢參數綁定提供了豐富的支持,那么什麼是查詢參數動態綁定呢?其實如果我們熟悉傳統JDBC編程的話,我們就不難理解查詢參數動態綁定,如下代碼傳統JDBC的參數綁定:PrepareStatementpre=connectionprepare(“select*fromUserwhereusername=?”);
presetString(1,”zhaoxin”);
ResultSetrs=preexecuteQuery();
在Hibernate中也提供了類似這種的查詢參數綁定功能,而且在Hibernate中對這個功能還提供了比傳統JDBC操作豐富的多的特性,在Hibernate中共存在4種參數綁定的方式。
A、按參數名稱綁定
在HQL語句中定義命名參數要用”:”開頭,形式如下:
Queryquery=sessioncreateQuery(“fromUseruserwhereusername=:customernameanduserage=:customerage”);
querysetString(“customername”,name);
querysetInteger(“customerage”,age);
上面代碼中用:customername和:customerage分別定義了命名參數customername和customerage,然後用Query接口的setXXX()方法設定名參數值,setXXX()方法包含兩個參數,分別是命名參數名稱和命名參數實際值。
B、按參數位置綁定
在HQL查詢語句中用”?”來定義參數位置,形式如下:
Queryquery=sessioncreateQuery(“fromUseruserwhereusername=?anduserage=?”);
querysetString(0,name);
querysetInteger(1,age);
同樣使用setXXX()方法設定綁定參數,只不過這時setXXX()方法的第一個參數代表邦定參數在HQL語句中出現的位置編號(由0開始編號),第二個參數仍然代表參數實際值。註:在實際開發中,提倡使用按名稱邦定命名參數,因為這不但可以提供非常好的程式可讀性,而且也提高了程式的易維護性,因為當查詢參數的位置發生改變時,按名稱邦定名參數的方式中是不需要調整程式代碼的。
C、setParameter()方法
在Hibernate的HQL查詢中可以通過setParameter()方法邦定任意類型的參數,如下代碼:
Stringhql=”fromUseruserwhereusername=:customername”;
Queryquery=sessioncreateQuery(hql);
querysetParameter(“customername”,name,HibernateSTRING);
如上面代碼所示,setParameter()方法包含三個參數,分別是命名參數名稱,命名參數實際值,以及命名參數映射類型。對於某些參數類型setParameter()方法可以根據參數值的Java類型,猜測出對應的映射類型,因此這時不需要顯示寫出映射類型,像上面的例子,可以直接這樣寫:querysetParameter(“customername”,name);但是對於一些類型就必須寫明映射類型,比如javautilDate類型,因為它會對應Hibernate的多種映射類型,比如HibernateDATE或者HibernateTIMESTAMP。
D、setProperties()方法
在Hibernate中可以使用setProperties()方法,將命名參數與一個對象的屬性值綁定在一起,如下程式代碼:
Customercustomer=newCustomer();
customersetName(“pansl”);
customersetAge(80);
Queryquery=sessioncreateQuery(“fromCustomercwherecname=:nameandcage=:age”);
querysetProperties(customer);
setProperties()
方法會自動將customer對象實例的屬性值匹配到命名參數上,但是要求命名參數名稱必須要與實體對象相應的屬性同名。E、使用綁定參數的優勢 我們為什麼要使用綁定命名參數?任何一個事物的存在都是有其價值的,具體到綁定參數對於HQL查詢來說,主要有以下兩個主要優勢:
①、可以利用資料庫實施性能最佳化,因為對Hibernate來說在底層使用的是PrepareStatement來完成查詢,因此對於語法相同參數不同的SQL語句,可以充分利用預編譯SQL語句快取,從而提升查詢效率。
②、可以防止SQLInjection安全漏洞的產生去意義。

相關詞條

相關搜尋

熱門詞條

聯絡我們