复杂搜索查询JPA

时间:2010-10-22 13:15:29

标签: java annotations jpa-2.0 criteria-api

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

3 个答案:

答案 0 :(得分: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连接的地方使用。

答案 1 :(得分:1)

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

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

答案 2 :(得分:1)

两种方法:

1 ..根据您需要的过滤方式,您可以通过搜索以下内容来实现此目的。使用Lucene索引所有对象,然后使用搜索查询来执行过滤。例如,建立一个像:

的查询

标题:“正确的方式”&amp; 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动态查找器

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