Hibernate的動態條件查詢DetachedCriteria

在常規的Web編程中,有大量的動態條件查詢,即用戶在網頁上面自由選擇某些條件,程序根據用戶的選擇條件,動態生成SQL語句,進行查詢。

  針對這種需求,對於分層應用程序來說,Web層需要傳遞一個查詢的條件列表給業務層對象,業務層對象獲得這個條件列表之後,然後依次取出條件,構造查詢語句。這裏的一個難點是條件列表用什麼來構造?傳統上使用Map,但是這種方式缺陷很大,Map可以傳遞的信息非常有限,只能傳遞name和value,無法傳遞究竟要做怎樣的條件運算,究竟是大於,小於,like,還是其它的什麼,業務層對象必須確切掌握每條entry的隱含條件。因此一旦隱含條件改變,業務層對象的查詢構造算法必須相應修改,但是這種查詢條件的改變是隱式約定的,而不是程序代碼約束的,因此非常容易出錯。

   DetachedCriteria可以解決這個問題,即在web層,程序員使用DetachedCriteria來構造查詢條件,然後將這個 DetachedCriteria作爲方法調用參數傳遞給業務層對象。而業務層對象獲得DetachedCriteria之後,可以在session範圍內直接構造Criteria,進行查詢。就此,查詢語句的構造完全被搬離到web層實現,而業務層則只負責完成持久化和查詢的封裝即可,與查詢條件構造完全解耦,非常完美!


   Criteria 和 DetachedCriteria 的主要區別在於創建的形式不一樣, Criteria 是在線的,所以它是由 Hibernate Session 進行創建的;而 DetachedCriteria 是離線的,創建時無需Session ,DetachedCriteria 提供了 2 個靜態方法 forClass(Class) 或 forEntityName(Name)進行DetachedCriteria 實例的創建。
   Spring 的框架提供了getHibernateTemplate().findByCriteria(detachedCriteria) 方法可以很方便地根據DetachedCriteria 來返回查詢結果。

 

Criteria: 
查詢User表格中的所有資料:

Criteria criteria = session.createCriteria(User.class);
// 查詢user所有欄位
List users = criteria.list();
Iterator iterator =  users.iterator();
while(iterator.hasNext()) {
    User user = (User) iterator.next();
    System.out.println(user.getId() + user.getName());           
}

Hibernate實際上使用以下的SQL來查詢:
select this_.id as id0_, this_.name as name0_0_ from user this_

Criteria只是個容器,如果想要設定查詢條件,則要使用add()方法加入Restrictions的條件限制 ,例如查詢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();



您也可以使用邏輯組合來進行查詢,例如結合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();

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



DetachedCriteria的關聯查詢:

假設要通過stuName查詢一個學生Student記錄,可以如下: 

</pre><pre name="code" class="java">DetachedCriteria dc = DetachedCriteria.forClass(Student.class); 
    dc.add(Restrictions.like("stuName", stuName, MatchMode.ANYWHERE)); 


      
如果要通過Student的Team的teamName查詢一個Student記錄,很多人都會這麼寫: 
DetachedCriteria dc = DetachedCriteria.forClass(Student.class); 
    dc.add(Restrictions.like("team.teamName", teamName, MatchMode.ANYWHERE)); 


      
遺憾的是上述程序報錯,說是在Student中找不到team.teamName屬性,這是可以理解的。那麼如何通過teamName查找Student呢? 
可以這麼寫: 
DetachedCriteria dc = DetachedCriteria.forClass(Student.class); 
    dc.createAlias("team", "t"); 
    dc.add(Restrictions.like("t.teamName", teamName, MatchMode.ANYWHERE)); 


      沒錯,就是要先建立team的引用,才能用team導航到teamName 。 

這裏有一個特殊情況,如果是對引用對象的id查詢,則可以不用建立引用,也就是可以不調用createAlias()語句,如下所示: 
DetachedCriteria dc = DetachedCriteria.forClass(Student.class); 
    dc.add(Restrictions.like("team.id", teamId, MatchMode.ANYWHERE)); 


      據我個人的經驗,team後只能跟其主鍵屬性,比較其他屬性要用別名。此主鍵屬性可以用“id”字符來指代,也可以用team的主鍵屬性來指代。換句話 說,我的Student類的類主鍵“stuId”,不管是在HQL還是在QBC中,都可以用stu.id來指代stu.stuId。在這裏可以看出 “id”字符的特殊性。上述是個人觀點,並未得到確實的證實。


DetachedCriteria:

例子1:如果每個美女都有自己的客戶資源(不要想歪了!),那麼需要查詢擁有客戶Gates的美女怎麼辦?
兩種方法:
1:

DetachedCriteria beautyCriteria = DetachedCriteria.forClass(Beauty.class).createCriteria("customers");
beautyCriteria.add(Restrictions.eq("name", "Gates")):



2:
DetachedCriteria beautyCriteria = DetachedCriteria.forClass(Beauty.class).createAlias("customers", "c");
beautyCriteria.add(Restrictions.eq("c.name", "Gates")):



接着有了新的要求,年紀太大的美女不要,還是查找擁有客戶Gates的,條件如下:
DetachedCriteria beautyCriteria = DetachedCriteria.forClass(Beauty.class, "b").;
DetachedCriteria customerCriteria = beautyCriteria.createAlias("customers", c");
beautyCriteria.add(Restrictions.le("b.age", new Long(20))):
customerCriteria.add(Restrictions.eq("c.name", "Gates")):



關於Criteria更詳細的資料,Hibernate的源代碼和測試是最好的文檔。
Criteria的缺點?DBA很生氣,後果很嚴重。


例子2:

Department和Employee是一對多關聯,查詢條件爲: 
  名稱是“department”開發部門; 
  部門裏面的僱員年齡大於20歲;

DetachedCriteria detachedCriteria = DetachedCriteria.forClass(Department.class);
detachedCriteria.add(Restrictions.eq("name", "department"))
                 .createAlias("employees", "e")
                 .add(Restrictions.gt(("e.age"), new Integer(20)));

List  list = this.getHibernateTemplate().findByCriteria(detachedCriteria);

文章轉載至:http://uule.iteye.com/blog/947923


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章