【问题标题】:Complex Search Queries JPA复杂的搜索查询 JPA
【发布时间】:2010-10-22 13:15:28
【问题描述】:

在我的 Wicket+JPA/Hibernate+Spring 项目中,大部分功能都基于收件箱页面,在该页面中,使用许多过滤选项(并非必须使用所有选项),用户可以限制他们想要的对象集跟...共事。我想知道实现这种过滤的最佳策略是什么?在此应用程序的旧版本中,搜索查询是通过连接包含 SQL 条件的字符串构建的。最近我读到了 JPA 提供的新 Criteria API - 你会推荐这个而不是使用搜索字符串吗?以及这如何与 DAO 层结合 - 在业务层中使用 Criteria API 构建搜索查询不是违反层分离吗?

【问题讨论】:

    标签: java annotations jpa-2.0 criteria-api


    【解决方案1】:

    对于像您描述的过滤查询,我绝对推荐使用 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 中加入的地方。

    【讨论】:

    • +1 表示基本概念。尽管有人可能会争论 customerNameOrID 参数的用法。它看起来像是将“或”嵌套到其他与“和”相关的参数中。如果出现数据类型相等的情况,这可能会变得任意复杂。对于这些情况,最好使用不同的参数集创建重载。
    • @Adriaan Koster 您的参数列表可以替换为吸引您搜索的类,例如 VehicleRentalContractCriteria
    • @Martin 'customerNameOrID' 来自一个文本输入字段,用户可以在其中输入客户名称的一部分或客户 ID。我不太明白您建议如何简化嵌套 OR,请告诉我们。
    • @Arthur 如果参数的数量很大(7 个或更多),或者如果参数的子集明显相关,我通常会考虑创建参数包装器。我认为使用包装器是一个逃生舱口,而不是一个优雅的解决方案。
    • 值得一提的是,这个答案使用的是 Hibernate Criteria API,而不是 JPA。
    【解决方案2】:

    即使我更喜欢使用 Criteria 而不是 HQL 和 SQL,对我来说原因是模块化和性能,因为当项目投入生产时,我们面临的主要问题是性能,HQL 和 SQL 都无法与 Criteria 竞争性能.

    添加到上面 DAO层是为了访问数据而创建的,这个层应该像玻璃一样清晰,没有任何复杂的编码或业务逻辑,但如果是标准,则必须编写一个逻辑(创建标准)来找到了一种更好的和优化的访问对象的方式,所以在我看来,将这么多逻辑放在 DAO 层中并没有违反。

    【讨论】:

    • 我认为任何高级 API 都无法胜过 SQL,因为它最终都以 SQL 查询告终。
    【解决方案3】:

    两种方法:

    1.. 根据您需要的过滤类型,您可以通过搜索来实现这一点,例如使用 Lucene 索引所有对象,然后使用搜索查询来执行过滤。例如建立一个查询,如:

    title:"The Right Way" & mod_date:[20020101 TO 20030101]

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


    2.. 或使用标准...

    我会使用来自 hibernate 的新类型安全标准 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动态查找器

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-10-18
      • 1970-01-01
      • 1970-01-01
      • 2022-01-23
      • 1970-01-01
      • 1970-01-01
      • 2012-08-07
      • 1970-01-01
      相关资源
      最近更新 更多