Hibernate_HQL--實體、屬性查詢,引數繫結,引用查詢(隨時溫習一遍)
是Hibernate官方推薦的查詢模式,比Criteria功能更強大。
1) 實體查詢:出現類名和屬性名必須注意大小寫區分;當不同路徑下存在相同類名,需要寫入在hql中寫入包名;查詢目標實體存在著繼承關係,將查詢目標的所有子類的庫表記錄一起返回。
String hql = “from TUser”;
Query query = session.createQuery(hql);
List list = query.list();
2) 屬性查詢:有時頁面不需要取整個物件,而只取某個屬性。
List list = session.createQuery(“select user.name user.age from TUser user”).list();
Iterator it = list.iterator();
while(it.hasNext())
{
//返回的list中,每個條目都是一個物件陣列,依次包含我們所獲取的資料。
Object[] results = (Object[])it.next();
System.out.println(results[0]);
System.out.println(results[1]);
}
注:如果覺得返回陣列的方式不夠靈活,可以在HQL中構造物件例項。
List list = this.session.createQuery(“select new TUser(user.name,user.age) from TUser user”).list();
Iterator it = list.iterator();
while(it.hasNext())
{
TUser user = (TUser)it.next();
System.out.println(user.getName());
}
注:通過HQL動態構造物件例項,此時查詢結果中的TUser物件只是一個Java物件,僅用於對查詢結果的封裝,除了在構造時賦予的屬性值之外,其他屬性均為未賦值狀態,當我們通過session對此物件進行更新,將導致對user物件的資料庫插入一條新資料,而不是更新原有物件。
我們也可以在HQL的select子句中使用統計函式(count(*),min(user.age))、SQL函式(upper(user.name))、distinct關鍵字。
1) 引數繫結
在HQL語句中直接寫入where語句後面的條件值,不能滿足引數為變數,如果引數設定變數,存在以下缺陷:a)編碼凌亂,可讀性降低;b)難以進行效能優化,JDBC以及資料庫操作原理,每次執行SQL,資料庫都將對SQL語法解析和優化,將處理結果儲存在快取中,以後引數不同語法相同的SQL命令直接以快取結果加以執行,從而避免了SQL解析和優化的開銷,如果傳入具體值,根據值不同將視為兩個SQL語句,無法利用快取提高效能;c)引入額外的安全風險,where name=’”+username+”’ and password=’”+password+”’”;在登入網頁上輸入使用者名稱:“Eric ’or‘ x ’=’ x”密碼隨意,根據輸入拼裝的HQL語句是where name=’Cartier’ or ‘x’=’x’ and password=’arbitrary’,由於被新增進了or ‘x’=’x,所以系統登入成功。這就是SQL Injection攻擊的基本原理。
Hibernate 引數繫結
//Session.find方法中填充引數
session.find(“from TUser where name=?”,”Erica”,Hibernate.STRING);
//多引數情況
Object[] args = new Object[]{“Erica”,new Integer(20)};
Type[] types = new Type[]{Hibernate.STRING,Hibernate.INTEGER};
session.find(“from TUser where name=? and age>?”,args,types);
//引用佔位符
String hql = “from TUser where name=:name”;
Query query = session.createQuery(hql);
query.setParameter(“name”,”Erica”);
Iterator it = query.list().iterator();
while(it.hasNext())
{
TUser user = (TUser)it.next();
System.out.println(user.getName());
}
注:“:name”就是引用佔位符,標識一個名為“name”的查詢引數,setParameter對引數進行填充。
//用JavaBean封裝查詢引數
class UserQuery{
private String name;
private Integer age;
………getter/setter
}
String hql =” from TUser where name=:name and age=:age”;
Query query = session.createQuery(hql);
UserQuery uq = new UserQuery();
uq.setName(“Erica”);
uq.setAge(new Integer(20));
query.setProperties(uq);
Iterator it = query.iterate();
while(it.hasNext())
{
TUser user = (TUser)it.next();
System.out.println(user.getName());
}
引用查詢:有些程式碼不允許寫sql語句,防止破壞程式碼可讀性。避免這種情況,採取將SQL配置化的方式,即將SQL儲存在配置檔案中,需要呼叫的時候在進行讀取。Hibernate提供了HQL可配置化的內建支援。可以在實體對映檔案裡,與class節點同級,定義查詢語句:
<query name=”queryByName”>
<![CDATA[from TUser user where user.name=:name]]>
</query>
Query query = session.getNameQuery(“queryByName”);
UserQuery uq = new UserQuery();
uq.setName(“Erica”);
query.setProperties(uq);
Iterator it = query.iterate();
while(it.hasNext())
{
TUser user = (TUser)it.next();
System.out.println(user.getName());
}
Hibernate:HQL語句講解及用法例項
近來專案裡的資料訪問層使用到了OR Mapping技術,為了縮短專案開發週期和成本,專案經理採取了現有的流行的Hibernate.在這裡,為了給廣大的Hibernate或 NHibernate的使用者提供學習查閱方便,特整理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實體操作體系中的核心地位,這一節我將專門圍繞HQL操作的具體技術細節進行講解。
1、 實體的更新和刪除:
在繼續講解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();
如果你是逐個章節閱讀的化,那麼你一定會記起我在第二部分中有關批量資料操作的相關論述中,討論過這種操作方式,這種操作方式在Hibernate3中稱 為 bulk delete/update,這種方式能夠在很大程度上提高操作的靈活性和執行效率,但是採用這種方式極有可能引起快取同步上的問題(請參考相關論述)。
2、實體查詢:
有關實體查詢技術,其實我們在先前已經有多次涉及,比如下面的例子:
String hql=”from User user ”;
List 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%’;
3、屬性查詢:
很多時候我們在檢索資料時,並不需要獲得實體物件所對應的全部資料,而只需要檢索實體物件的部分屬性所對應的資料。這時候就可以利用HQL屬性查詢技術,如下面程式示例:
List list=session.createQuery(“select user.name from User user ”).list();
for(int i=0;i
System.out.println(list.get(i));
}
我們只檢索了User實體的name屬性對應的資料,此時返回的包含結果集的list中每個條目都是String型別的name屬性對應的資料。我們也可以一次檢索多個屬性,如下面程式:
List list=session.createQuery(“select user.name,user.age from User user ”).list();
for(int i=0;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
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
User user=(User)list.get(i);
user.setName(“gam”);
session.saveOrUpdate (user);//這裡將會實際執行一個save操作,而不會執行update操作,因為這個User物件的id屬性為null,Hibernate會把 它作為一個自由物件(請參考持久化物件狀態部分的論述),因此會對它執行save操作。
}
4、分組與排序
A、Order by子句:
與SQL語句相似,HQL查詢也可以通過order by子句對查詢結果集進行排序,並且可以通過asc或者desc關鍵字指定排序方式,如下面的程式碼:
from User user order by user.name asc,user.age desc;
上面HQL查詢語句,會以name屬性進行升序排序,以age屬性進行降序排序,而且與SQL語句一樣,預設的排序方式為asc,即升序排序。
B、Group by子句與統計查詢:
在HQL語句中同樣支援使用group by子句分組查詢,還支援group by子句結合聚集函式的分組統計查詢,大部分標準的SQL聚集函式都可以在HQL語句中使用,比如: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、優化統計查詢:
假設我們現在有兩張資料庫表,分別是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 ;(1)
select c.ID,c.name,c.age,o.ID,o.order_number,o.customer_ID
from Customer c inner join c.orders c ;(2)
這兩條語句使用了HQL語句的內連線查詢(我們將在HQL語句的連線查詢部分專門討論),現在我們可以看出這兩條查詢語句最後所返回的結果是一樣的,但是它們其實是有明顯區別的,語句(1)檢索的結果會返回Customer與Order持久化物件,而且它們會被置於Hibernate的Session快取 之中,並且Session會負責它們在快取中的唯一性以及與後臺資料庫資料的同步,只有事務提交後它們才會從快取中被清除;而語句(2)返回的是關係資料 而並非是持久化物件,因此它們不會佔用Hibernate的Session快取,只要在檢索之後應用程式不在訪問它們,它們所佔用的記憶體就有可能被JVM 的垃圾回收器回收,而且Hibernate不會同步對它們的修改。
在我們的系統開發中,尤其是Mis系統,不可避免的要進行統計查詢的開發,這類功能有兩個特點:第一資料量大;第二一般情況下都是隻讀操作而不會涉及到對統計資料進行修改,那麼如果採用第一種查詢方式,必然會導致大量持久化物件位於 Hibernate的Session快取中,而且Hibernate的Session快取還要負責它們與資料庫資料的同步。而如果採用第二種查詢方式,顯 然就會提高查詢效能,因為不需要Hibernate的Session快取的管理開銷,而且只要應用程式不在使用這些資料,它們所佔用的記憶體空間就會被回收釋放。
因此在開發統計查詢系統時,儘量使用通過select語句寫出需要查詢的屬性的方式來返回關係資料,而避免使用第一種查詢方式返回持久化物件(這種方式是在有修改需求時使用比較適合),這樣可以提高執行效率並且減少記憶體消耗。㊣真正的高手並不是精通一切,而是精通在合適的場合使用合適的手段。
5、引數繫結:
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種引數繫結的方式,下面我們將分別介紹:
A、 按引數名稱繫結:
在HQL語句中定義命名引數要用”:”開頭,形式如下:
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分別定義了命名引數customername和customerage,然後用 Query介面的setXXX()方法設定名引數值,setXXX()方法包含兩個引數,分別是命名引數名稱和命名引數實際值。
B、 按引數位置邦定:
在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開始編號),第二個引數仍然代表引數實際值。
注:在實際開發中,提倡使用按名稱邦定命名引數,因為這不但可以提供非常好的程式可讀性,而且也提高了程式的易維護性,因為當查詢引數的位置發生改變時,按名稱邦定名引數的方式中是不需要調整程式程式碼的。
C、 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。
D、 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’;
E、 使用繫結引數的優勢:
我們為什麼要使用繫結命名引數?任何一個事物的存在都是有其價值的,具體到繫結引數對於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安全漏洞。
在hibernate的配置檔案中配置hql查詢語句
<hibernate-mapping>
<class name="org.tie.User" table="user" catalog="tie">
<id name="id" type="long">
<column name="id" />
<generator class="native" />
</id>
<property name="name" type="string">
<column name="name" length="45" not-null="true" />
</property>
<property name="age" type="integer">
<column name="age" not-null="true" />
</property>
<property name="addr" type="string">
<column name="addr" length="45" not-null="true" />
</property>
</class>
<!-- 這裡將Hql語句寫到配置檔案當中,名字是可以隨便取的 -->
<query name="haha">
from User where addr=:address and age=:age
</query>
</hibernate-mapping>
java 程式碼
/*
* 另外Hibernate允許我們把 sql語句配置到檔案中
* 因為寫到程式中需要編譯的,而寫到配置檔案中是不需要編譯的
*/
public void testHQL13(){
Session session = factory.openSession();
//這裡通過getNameQuery這個方法來取得到配置檔案中的hql語句
Query query = session.getNamedQuery("haha");
query.setString("address", "fujian");
query.setInteger("age", 22);
List<User> users = query.list();
for(User user : users){
System.out.println(user.getName());
System.out.println("---------------");
}
session.close();
}
相關文章
- 查詢繫結變數的值變數
- ES查詢之查詢屬性過濾、結果高亮顯示
- 開啟查詢慢查詢日誌引數
- oracle 查詢未使用繫結變數的sqlOracle變數SQL
- 線性時間查詢
- 如何實現引數級聯查詢
- 通過clss屬性查詢元素
- mysql實現隨機查詢MySql隨機
- 查詢hadoop引數變數Hadoop變數
- 隱含引數的查詢
- 隱藏引數查詢sqlSQL
- 查詢沒有使用繫結變數的sql zt變數SQL
- PostgreSQL實時高效搜尋-全文檢索、模糊查詢、正則查詢、相似查詢、ADHOC查詢SQL
- Laravel同時接收路由引數和查詢字串中的引數Laravel路由字串
- V$sql查詢未使用繫結變數的語句SQL變數
- 【效能優化】查詢繫結變數的sql語句優化變數SQL
- Sql Server 的引數化查詢SQLServer
- 抽象SQL引數化查詢VK抽象SQL
- Oracle隱含引數的查詢Oracle
- 查詢演算法集:順序查詢、二分查詢、插值查詢、動態查詢(陣列實現、連結串列實現)演算法陣列
- 輿情繫統查詢
- MySQL聯結查詢和子查詢MySql
- MYSQL學習筆記25: 多表查詢(子查詢)[標量子查詢,列子查詢]MySql筆記
- IP查詢類API介面查詢,含各精度IP歸屬地查詢介面API
- 透過手機號查詢繫結QQ
- 深入理解 Python 的屬性查詢Python
- mysql查詢結果多列拼接查詢MySql
- Microsoft Graph for Office 365 - 查詢引數(二)ROS
- Microsoft Graph for Office 365 - 查詢引數(一)ROS
- MySQL查詢快取引數詳解MySql快取
- MySQL引數化查詢的IN 和 LIKEMySql
- Oracle隱形引數查詢指令碼Oracle指令碼
- 獲取request中的查詢引數
- 查詢oracle中的隱形引數Oracle
- 隱藏引數查詢和dictionary viewView
- oracle查詢結果外面新增引號Oracle
- EF Core 三 、 騷操作 (導航屬性,記憶體查詢...)記憶體
- Hadoop - 實時查詢DrillHadoop