2010-10-22 139 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动态查找

如果你真的愿意,你可以实现一个简单的语法层的完全分离。然后解析这个来创建相关的标准。这将允许更改基础标准实现。这是否合适取决于这种抽象对你有多重要。