2013年11月25日 星期一

DetachedCriteria 範例程式介紹(一) - 對照SQL Query解說

隔了半年,才又拿出時間記錄點學習歷程,希望這次能夠持之以恆阿...話說有時候真的是很懶惰,有些東西想記下來,卻又老是有一堆不寫的理由XD...

廢話不多說,今天主要想記錄的是 DetachedCriteria的應用。相信許多人在使用Hibernate時,都會比較習慣使用HQL來對資料庫下查詢指令,因為它跟Sql Query非常相似,基本上會Sql Query,幾乎就會使用HQL。但問題是code不總是自己寫,還要維護前人或其他人的code,也因此總還是會有機會去碰觸使用Criteria寫的資料庫查詢程式,所以索性還是耐著性子,上網爬了點文,將自己試跑過的DetachedCriteria的一些相關功能做個整理與紀錄。

在接下來的介紹中,打算以 DetachedCrititeria的實例程式對照轉譯成SQL後的樣子來進行解說,對我個人來說,這樣是學最快也是最好記憶的方式,若想知道一些個別介面的詳細使用說明,網路上有許多文可以爬,例如這篇有很詳細的DetachedCriteria的完整說明或是去查詢Hibernate API
 第一個detachedCriteria實例:
  Property versionNumberPro=Property.forName("versionNumber");
  Property apIdPro=Property.forName("apId");
  DetachedCriteria mainQuery= DetachedCriteria.forClass(AgentAp.class)
  .addOrder(Order.desc("apId"));
  DetachedCriteria subQuery=DetachedCriteria.forClass(AgentAp.class);
  subQuery.setProjection(Property.forName("apId"))
  .add(Restrictions.disjunction().add(versionNumberPro.eq(new String("1.0.1")))
    .add(versionNumberPro.eq(new String("1.0.2"))))
  .add(Restrictions.conjunction().add(apIdPro.ge(new Integer(114)))
          .add(apIdPro.le(new Integer(128))));

List listAps= mainQuery.add(Property.forName("apId").in(subQuery))
  .getExecutableCriteria(this.getSession()).list();//
Hibernate 翻譯出來的 sql長的樣子如下:因此我們可以從下面sql開始對照DetachedCriteria指令。
 
    select
        this_.AP_ID as AP1_84_0_,
        this_.IS_UPDATE as IS2_84_0_,
        this_.VERSION_DATE as VERSION3_84_0_,
        this_.VERSION_NUMBER as VERSION4_84_0_ 
    from
        AGENT_AP this_ 
    where
        this_.AP_ID in (
            select
                max(this_.AP_ID) as y0_ 
            from
                AGENT_AP this_ 
            where
                (
                    this_.VERSION_NUMBER=? 
                    or this_.VERSION_NUMBER=?
                ) 
                and (
                    this_.AP_ID>=? 
                    and this_.AP_ID<=?
                )
        ) 
    order by
        this_.AP_ID desc
首先 DetachedCriteria mainQuery= DetachedCriteria.forClass(AgentAp.class) ,
就是sql中的
    select
    this_.AP_ID as AP1_84_0_,
        this_.IS_UPDATE as IS2_84_0_,
        this_.VERSION_DATE as VERSION3_84_0_,
        this_.VERSION_NUMBER as VERSION4_84_0_ 
    from
        AGENT_AP this_

接下來,由於我的這個主查詢需要根據子查詢的結果當作主查詢的條件,因此在Criteria程式中,我會建立一個相當於子查詢功能(在sql中)的subQuery(一樣是DetachedCriteria類別): DetachedCriteria subQuery=DetachedCriteria.forClass(AgentAp.class);
建立好子查詢的DetachedCriteria後,接下來我們要考慮的是,主查詢是要以這個子查詢的哪個欄位來當作條件依據,在本例子中,是要以"apId"這個entity的property(與實際在db中的field name不一定相等,看各位自己名字的取法),因此會有這句的設定: subQuery.setProjection(Property.forName("apId").max()) 剛開始看這些東西,真的會覺得很不習慣,為什麼要這樣取,不過其實也沒太多為什麼,寫多看久,自然就知道要這樣用,而透過subQuery.setProjection(Property.forName("apId")設定好要select出來的欄位後,大家有注意到後面還有個max()方法,它其實就是取前面那個欄位名稱的最大值,到目前為止,所看到的Criteria程式,剛好就可以翻譯成
 select max(this_.AP_ID) as y0_ from AGENT_AP this_ 這樣的一段sql。

在這段子查詢中,我們也必須根據一些條件去過濾它,因此會有後面這段
.add(Restrictions.disjunction().add(versionNumberPro.eq(new String("1.0.1")))
    .add(versionNumberPro.eq(new String("1.0.2"))))
.add(Restrictions.conjunction().add(apIdPro.ge(new Integer(114))).add(apIdPro.le(new Integer(128))));
看起來好像有點小複雜,其實轉成sql就是下面這段:
where ( this_.VERSION_NUMBER=?
           or
            this_.VERSION_NUMBER=? )
 and (   this_.AP_ID>=?
           and
           this_.AP_ID<=? )
其中,Restrictions.disjunction()就是指我們接下來add的條件都是or起來的,如上面對VERSION_NUMBER 條件的or操作,
而Restrictions.conjunction()就是指接下來的條件都是and起來的,如上面對AP_ID的操作。
到現在為止,子查詢的部分已經解釋完畢,接下來就是將主查詢與子查詢給串起來,相信看到這邊在回顧一下最上面的Criteria程式,也能猜到是這句
mainQuery.add(Property.forName("apId").in(subQuery)
其實這部分還算滿直覺的,就是將主查詢加入一個子查詢,其中查詢的依據就是依據"apId"這個欄位,產生的sql如下這,紅色標記的部分當然就是子查詢了。
                where  this_.AP_ID in ( select max(this_.AP_ID) as y0_ 
                                                              from AGENT_AP this_ 
                                                              where ( this_.VERSION_NUMBER=? 
                                                                           or this_.VERSION_NUMBER=? )
                                                              and     ( this_.AP_ID>=? 
                                                                          and this_.AP_ID<=? ) 
                                                     )              
                 order by this_.AP_ID desc
奇怪,這個 order by 是怎麼來的,好像漏說了,恩對,確實沒說,這個order by 是一開始在建立主要的mainQuery時就指定,
DetachedCriteria mainQuery= DetachedCriteria.forClass(AgentAp.class) .addOrder(Order.desc("apId"))。

至此透過一個簡單的DetachedCriteria範例程式與SQL Query的對照,大致可以掌握了解DetachedCriteria的基本常用的一些方法,當然其實還有非常多的東西沒有講到,希望能在近期陸續將更多實用的相關方法補齊。