1. 程式人生 > >Hibernate三種資料操作方式HQL-Criteria-Sql

Hibernate三種資料操作方式HQL-Criteria-Sql

以最基本的查詢來說,如果您想要查詢某個物件所對應的資料表中所有的內容,您可以如下進行查詢:


Criteria criteria = session.createCriteria(User.class);List users = criteria.list();


for(Iterator it = users.iterator(); it.hasNext(); ) {User user = (User) it.next();System.out.println(user.getId() +" \t " + user.getName() +"/" + user.getAge());}


Criteria建立後,若不給予任何的條件,預設是查詢物件所對應表格之所有資料,如果您執行以上的程式片段,並於設定檔中設定了了Hibernate的”show_sql”屬性,則可以在主控下看到以下的SQL語句之產生:


Hibernate: select this_.id as id0_0_, this_.name as name0_0_, this_.age as age0_0_ from T_USER this_


Criteria基本查詢條件設定


org.hibernate.Criteria實際上是個條件附加的容器,如果想要設定查詢條件,則要使用org.hibernate.criterion.Restrictions的各種靜態方法傳回org.hibernate.criterion.Criteria例項,傳回的每個org.hibernate.criterion.Criteria例項代表著一個條件,您要使用org.hibernate.Criteria的add()方法加入這些條件例項,例如查詢”age”大於20且小於40的資料:


Criteria criteria = session.createCriteria(User.class);criteria.add(Restrictions.gt("age", new Integer(20)));criteria.add(Restrictions.lt("age", new Integer(40)));List users = criteria.list();


for(Iterator it = users.iterator(); it.hasNext(); ) {User user = (User) it.next();System.out.println(user.getId() +" \t " + user.getName() +"/" + user.getAge());}


Restrictions的gt()方法表示大於(great than)的條件,而lt表示小於(less than)的條件,執行以上程式片段,觀察所產生的SQL語句,將使用where與and子句產來完成SQL的條件查詢:


Hibernate: select this_.id as id0_0_, this_.name as name0_0_, this_.age as age0_0_ from T_USER this_ where this_.age>? and this_.age


使用add()方法加入條件時,預設是使用and來組合條件,如果要用or的方式來組合條件,則可以使用Restrictions.or()方法,例如結合age等於(eq)20或(or)age為空(isNull)的條件:


Criteria criteria = session.createCriteria(User.class);criteria.add(Restrictions.or(Restrictions.eq("age", new Integer(20)),Restrictions.isNull("age")));List users = criteria.list();


觀察所產生的SQL語句,將使用where與or子句完成SQL的條件查詢:


Hibernate: select this_.id as id0_0_, this_.name as name0_0_, this_.age as age0_0_ from T_USER this_ where (this_.age=? or this_.age is null)


您也可以使用Restrictions.like()方法來進行SQL中like子句的功能,例如查詢”name”中名稱為”just”開頭的資料:


Criteria criteria = session.createCriteria(User.class);criteria.add(Restrictions.like("name", "just%"));List users = criteria.list();


觀察所產生的SQL語句如下:


Hibernate: select this_.id as id0_0_, this_.name as name0_0_, this_.age as age0_0_ from T_USER this_ where this_.name like ?


Restrictions的幾個常用限定查詢方法如下表所示:


方法 說明Restrictions.eq 等於Restrictions.allEq 使用Map,使用key/value進行多個等於的比對Restrictions.gt 大於 >Restrictions.ge 大於等於 >=Restrictions.lt 小於 <Restrictions.le 小於等於 <=Restrictions.between 對應SQL的BETWEEN子句Restrictions.like 對應SQL的LIKE子句Restrictions.in 對應SQL的in子句Restrictions.and and關係Restrictions.or or關係


Criteria進階查詢條件設定


使用Criteria進行查詢時,不僅僅能組合出SQL中where子句的功能,還可以組合出如排序、統計、分組等的查詢功能。


排序


您可以使用Criteria進行查詢,並使用org.hibernate.criterion.Order對結果進行排序,例如使用Oder.asc(),指定根據”age”由小到大排序(反之則使用desc()):


Criteria criteria = session.createCriteria(User.class);criteria.addOrder(Order.asc("age"));List users = criteria.list();


注意在加入Order條件時,使用的是addOrder()方法,而不是add()方法,在產生SQL語句時,會使用order by與asc(desc)來進行排序指定:


Hibernate: select this_.id as id0_0_, this_.name as name0_0_, this_.age as age0_0_ from T_USER this_ order by this_.age asc


限定查詢筆數


Criteria的setMaxResults()方法可以限定查詢回來的筆數,如果配合setFirstResult()設定傳回查詢結果第一筆資料的位置,就可以實現簡單的分頁,例如傳回第51筆之後的50筆資料(如果有的話):


Criteria criteria = session.createCriteria(User.class);criteria.setFirstResult(51);criteria.setMaxResults(50);List users = criteria.list();


根據您所指定得資料庫,Hibernate將自動產生與資料庫相依的限定筆數查詢子句,例如在MySQL中,將使用limit產生以下的SQL語句:


Hibernate: select this_.id as id0_0_, this_.name as name0_0_, this_.age as age0_0_ from T_USER this_ limit ?, ?


統計動作


您可以對查詢結果進行統計動作,使用org.hibernate.criterion.Projections的avg()、rowCount()、count()、max()、min()、 countDistinct()等方法,再搭配Criteria的setProjection()方法加入條件設定,例如對查詢結果的"age"作平均:


Criteria criteria = session.createCriteria(User.class);criteria.setProjection(Projections.avg("age"));List users = criteria.list();


上面的程式將由Hibernate自動產生SQL的avg函式進行平均計算:


Hibernate: select avg(this_.age) as y0_ from T_USER this_


分組


還可以配合Projections的groupProperty()來對結果進行分組,例如以"age"進行分組,也就是如果資料中"age"如果有 20、20、25、30,則以下會顯示20、25、30:


Criteria criteria = session.createCriteria(User.class);criteria.setProjection(Projections.groupProperty("age"));List users = criteria.list();


上面的程式將由Hibernate自動產生SQL的group by子句進行分組計算:


Hibernate: select this_.age as y0_ from T_USER this_ group by this_.age


如果想同時結合統計與分組功能,則可以使用org.hibernate.criterion.ProjectionList,例如下面的程式會計算每個年齡各有多少個人:


ProjectionList projectionList = Projections.projectionList();projectionList.add(Projections.groupProperty("age"));projectionList.add(Projections.rowCount());


Criteria criteria = session.createCriteria(User.class);criteria.setProjection(projectionList);List users = criteria.list();


觀察所產生的SQL語句,將使用group by先進行分組,再針對每個分組進行count函式的計數


Hibernate: select this_.age as y0_, count(*) as y1_ from T_USER this_ group by this_.age


根據已知物件進行查詢


設定查詢條件並非一定要使用Restrictions,如果屬性條件很多,使用Restrictions也不方便,如果有一個已知的物件,則可以根據這個物件作為查詢的依據,看看是否有屬性與之類似的物件,例如:


User user = new User();user.setAge(new Integer(30));


Criteria criteria = session.createCriteria(User.class);criteria.add(Example.create(user));


List users = criteria.list();


您可以透過org.hibernate.criterion.Example的create()方法來建立Example例項,Example實作了Criteria介面,因此可以使用add()方法加入至Criteria條件設定之中,Hibernate將自動過濾掉空屬性,根據已知物件上已設定的屬性,判定是否產生於where子句之中:


Hibernate: select this_.id as id0_0_, this_.name as name0_0_, this_.age as age0_0_ from T_USER this_ where (this_.age=?)


設定SQL範本


如果您瞭解如何撰寫SQL語句,想要設定一些Hibernate產生SQL時的範本,您也可以使用Restrictions的sqlRestriction()方法,提供SQL語法範本作限定查詢,例如查詢name以cater開頭的資料:


Criteria criteria = session.createCriteria(User.class);criteria.add(Restrictions.sqlRestriction("{alias}.name LIKE (?)", "cater%", Hibernate.STRING));List users = criteria.list();


其中alias將被替換為與User類別相關的名稱,而?將被替換為cater%,也就是第二個引數所提供的值,sqlRestriction()方法第一個引數所設定的是where子句的部份,所以在SQL撰寫時,不必再寫where,觀察所產生的SQL語句,將使用您所設定的SQL範本作為基礎,來完成SQL的條件查詢:


Hibernate: select this_.id as id0_0_, this_.name as name0_0_, this_.age as age0_0_ from T_USER this_ where this_.name LIKE (?)


如果有多個查詢條件,例如between子句的查詢,則可以如下:


Criteria criteria = session.createCriteria(User.class);Integer[] ages = {new Integer(20), new Integer(40)};Type[] types = {Hibernate.INTEGER, Hibernate.INTEGER};criteria.add(Restrictions.sqlRestriction("{alias}.age BETWEEN (?) AND (?)", ages, types));List users = criteria.list();


觀察所產生的SQL語句如下:


Hibernate: select this_.id as id0_0_, this_.name as name0_0_, this_.age as age0_0_ from T_USER this_ where this_.age BETWEEN (?) AND (?)


使用DetchedCriteria


Criteria與Session繫結,其生命週期跟隨著Session結束而結束,使用Criteria時進行查詢時,每次都要於執行時期動態建立物件,並加入各種查詢條件,隨著Session的回收,Criteria也跟著回收。


為了能夠重複使用Criteria物件,在Hibernate 3中新增了org.hibernate.criterion.DetchedCriteria,您可以先建立DetchedCriteria例項,並加入各種查詢條件,並於需要查詢時再與Session繫結,獲得一個繫結Session的Criteria物件,例如:


// 先建立DetchedCriteria物件DetachedCriteria detchedCriteria = DetachedCriteria.forClass(User.class);// 加入查詢條件detchedCriteria.add(Restrictions.ge("age",new Integer(25)));


Session session = sessionFactory.openSession();// 繫結Session並返回一個Criteria例項Criteria criteria = detchedCriteria.getExecutableCriteria(session);


List users = criteria.list();


結論


Hibernate的Criteria API可以讓您使用物件的方式,組合出查詢資料庫系統的條件,Hibernate會自動依您所使用的資料庫,動態產生SQL語句,讓您的應用程式在存取資料庫時,不致於因撰寫了特定的SQL而相依於特定的資料庫,如果您的開發人員不熟悉SQL語句的撰寫,也可以試著使用Criteria來解決查詢資料庫的需求。


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// Hibernate中的Criteria條件查詢Criteria Query通過面向物件化的設計,將資料查詢條件封裝為一個物件。簡單來講,Criteria Query可以看作是傳統SQL的物件化表示,如:


Criteria criteria = session.createCriteria(User.class);criteria.add(Expression.eq("name","Erica");criteria.add(Expression.eq("sex",new Integer(1)));


這裡的criteria 例項實際上是SQL “Select * from t_user where name=’Erica’ and sex=1”的封裝(我們可以開啟Hibernate 的show_sql 選項,


以觀察Hibernate在執行期生成的SQL語句)。


Hibernate 在執行期會根據Criteria 中指定的查詢條件(也就是上面程式碼中通過criteria.add方法新增的查詢表示式)生成相應的SQL語句。這種方式的特點是比較符合Java 程式設計師的編碼習慣,並且具備清晰的可讀性。正因為此,不少ORM實現中都提供了類似的實現機制(如Apache OJB)。


對於Hibernate的初學者,特別是對SQL瞭解有限的程式設計師而言,Criteria Query無疑是上手的極佳途徑,相對HQL,Criteria Query提供了更易於理解的查詢手段,藉助IDE的Coding Assist機制,Criteria的使用幾乎不用太多的學習。


Criteria 查詢表示式

Criteria 本身只是一個查詢容器,具體的查詢條件需要通過Criteria.add方法新增到Criteria例項中。如前例所示,Expression 物件具體描述了查詢條件。針對SQL 語法,Expression提供了對應的查詢限定機制,包括:

方法 描述

Expression.eq 對應SQL“field = value”表示式。如Expression.eq("name","Erica"

Expression.allEq 引數為一個Map物件,其中包含了多個屬性-值對應關係。相當於多個Expression.eq關係的疊加。

Expression.gt 對應SQL中的 “field > value ” 表示式

Expression.ge 對應SQL中的 “field >= value” 表示式

Expression.lt 對應SQL中的 “field < value” 表示式

Expression.le 對應SQL中的 “field <= value” 表示式

Expression.between 對應SQL中的 “between” 表示式

如下面的表示式表示年齡(age)位於13到50區間內。Expression.between("age",new Integer(13),new Integer(50));

表示式Expression.in 對應SQL中的 ”field in …” 表示式

Expression.eqProperty 用於比較兩個屬性之間的值,對應SQL中的“field= field”。如:Expression.eqProperty("TUser.groupID","TGroup.id";

Expression.gtProperty 用於比較兩個屬性之間的值,對應SQL中的“field> field”。

Expression.geProperty 用於比較兩個屬性之間的值,對應SQL中的“field>= field”。

Expression.ltProperty 用於比較兩個屬性之間的值,對應SQL中的“field< field”。

Expression.leProperty 用於比較兩個屬性之間的值,對應SQL中的“field<= field”。

Expression.and and關係組合。如:Expression.and(Expression.eq("name","Erica",Expression.eq("sex",new Integer(1)));

Expression.or or關係組合。如:Expression.or(Expression.eq("name","Erica",Expression.eq("name","Emma");

Expression.sql 作為補充,本方法提供了原生SQL語法的支援。我們可以通過這個方法直接通過SQL語句限定查詢條件。

下面的程式碼返回所有名稱以“Erica”起始的記錄:Expression.sql("lower({alias}.name) like lower(?)","Erica%",Hibernate.STRING);其中的“{alias}”將由Hibernate在執行期使用當前關聯的POJO別名替換。

注意Expression 各方法中的屬性名引數(如Express.eq中的第一個引數),這裡所謂屬性名是POJO中對應實際庫表字段的屬性名(大小寫敏感),而非庫表中的實際欄位名稱。

Criteria 高階特性

限定返回的記錄範圍通過criteria. setFirstResult/setMaxResults 方法可以限制一次查詢返回的記錄範圍:

Criteria criteria = session.createCriteria(TUser.class);//限定查詢返回檢索結果中,從第一百條結果開始的20條記錄criteria.setFirstResult(100);criteria.setMaxResults(20);

對查詢結果進行排序

//查詢所有groupId=2的記錄//並分別按照姓名(順序)和groupId(逆序)排序Criteria criteria = session.createCriteria(TUser.class);criteria.add(Expression.eq("groupId",new Integer(2)));criteria.addOrder(Order.asc("name");criteria.addOrder(Order.desc("groupId");

Criteria作為一種物件化的查詢封裝模式,不過由於Hibernate在實現過程中將精力更加集中在HQL查詢語言上,因此Criteria的功能實現還沒做到盡善盡美(這點上,OJB的Criteria 實現倒是值得借鑑),因此,在實際開發中,建議還是採用Hibernate 官方推薦的查詢封裝模式:HQL。