2010-10-22 173 views
10

在我的Wicket + JPA/Hibernate + Spring項目中,許多功能都基於收件箱頁面,在這個頁面中,使用許多過濾選項(並非所有過濾選項都必須使用),用戶可以限制設置他們想要使用的對象。我想知道實施這種過濾的最佳策略是什麼?在此應用程序的舊版本中,搜索查詢是通過串聯包含SQL條件的字符串構建的。最近我閱讀了關於JPA新提供的Criteria API - 你會推薦使用搜索字符串嗎?並且這與DAO層是如何結合在一起的 - 是不是使用業務層中的Criteria API構建搜索查詢,而是違反了層的分離?複雜搜索查詢JPA

回答

4

對於像你所描述的過濾查詢,我絕對推薦使用Hibernate或JPA標準API,因爲它支持條件查詢。我通常只是將標準構建代碼放入我的DAO中,並將所有必需的(可能爲null)參數傳遞到那裏。

下面是一個例子汽車租賃應用程序中使用Hibernate的標準API的例子DAO方法:

public List<VehicleRentalContract> list(Long contractID, 
      String customerNameOrID, Date date, 
      String vehicleDescriptionOrRegistration) { 
     Criteria criteria = getSession().createCriteria(
       VehicleRentalContract.class); 
     // contractID filter 
     if (contractID != null && contractID != 0) { 
      criteria.add(Restrictions.eq("id", contractID)); 
     } 
     // customerNameOrID filter 
     if (customerNameOrID != null && customerNameOrID.length() > 0) { 
      try { 
       Long customerID = Long.parseLong(customerNameOrID); 
       criteria.add(Restrictions.eq("customer.id", customerID)); 
      } catch (NumberFormatException e) { 
       // assume we have a customer name 
       String customerNameQuery = "%" + customerNameOrID.trim() + "%"; 
       criteria.createAlias("customer", "customer").add(
         Restrictions.or(Restrictions.like("customer.firstName", 
           customerNameQuery), Restrictions.like(
           "customer.lastName", customerNameQuery))); 
      } 
     } 
     // date filter 
     if (date != null) { 
      criteria.add(Restrictions.and(
        Restrictions.le("rentalPeriod.startDate", date), 
        Restrictions.ge("rentalPeriod.endDate", date))); 
     } 

     // vehicleDescriptionOrRegistration filter 
     if (vehicleDescriptionOrRegistration != null 
       && vehicleDescriptionOrRegistration.length() > 0) { 
      String registrationQuery = "%" 
        + Vehicle 
          .normalizeRegistration(vehicleDescriptionOrRegistration) 
        + "%"; 
      String descriptionQuery = "%" 
        + vehicleDescriptionOrRegistration.trim() + "%"; 

      criteria.createAlias("vehicle", "vehicle").add(
        Restrictions.or(Restrictions.like("vehicle.registration", 
          registrationQuery), Restrictions.like(
          "vehicle.description", descriptionQuery))); 
     } 

     List<VehicleRentalContract> contracts = criteria.list(); 
     return contracts; 
} 

的createAlias調用可以使用,你需要在SQL聯接。

+0

+1爲基本概念。儘管可以爭論使用customerNameOrID參數。它看起來像嵌套一個「或」成其他「和」相關的參數。如果遇到數據類型相同的情況,這可能變得任意複雜。對於這些情況,最好使用不同的參數集創建重載。 – 2010-10-27 13:41:19

+0

@Adriaan Koster您的參數列表可以替換爲繪製搜索的類,例如** VehicleRentalContractCriteria ** – 2010-10-31 16:49:57

+0

@Martin'customerNameOrID'來自文本輸入字段,用戶可以在其中輸入客戶的一部分名稱或客戶ID。我不太瞭解你如何建議簡化嵌套OR,請告訴我們。 – 2010-11-10 08:02:59

1

即使我更喜歡使用Criteria而不是HQL和SQL,對我而言,理由將是模塊化和性能,因爲當項目投入生產時,我們面臨的主要問題是性能,無論是HQL還是SQL都無法與Criteria競爭性能。

添加到上面DAO層是爲訪問數據而創建的,該層應該像玻璃一樣清晰,沒有任何複雜的編碼或業務邏輯,但是在標準情況下,必須編寫一個邏輯(創建標準)以獲得更好的和調整的訪問對象的方式,所以在我看來,在DAO層中放置這麼多的邏輯沒有任何缺點。

+0

我不認爲任何高級API都可能超越SQL,因爲它畢竟都是SQL查詢。 – 2010-11-10 08:10:33

1

兩種方法:

1 ..根據什麼類型的篩選,你需要你可以通過例如搜索以達致這使用Lucene索引所有對象,然後使用搜索查詢執行過濾。例如,建立一個查詢,如:

標題: 「正確的方式」 & mod_date:[20020101 TO 20030101]

參見:http://lucene.apache.org/java/2_4_0/queryparsersyntax.html


2 ..或者用標準...

我會使用新的類型安全標準API從休眠狀態:

http://relation.to/12805.lace

是建立了一個非常大的標準,而不是一個方法,我會嘗試使用分離的標準來單獨出所有的邏輯 -

http://docs.jboss.org/hibernate/core/3.5/reference/en/html/querycriteria.html#querycriteria-detachedqueries

有了這兩者的組合你就可以建立輕鬆制定標準。

另一個尋找靈感的地方是grails動態發現者。這基本上是你試圖以靜態方式實現的。

http://www.grails.org/doc/1.0.x/guide/single.html#5.4.1動態查找

如果你真的願意,你可以實現一個簡單的語法層的完全分離。然後解析這個來創建相關的標準。這將允許更改基礎標準實現。這是否合適取決於這種抽象對你有多重要。