复杂搜索查询 JPA

发布于 2024-09-28 14:41:54 字数 261 浏览 7 评论 0 原文

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

In my Wicket+JPA/Hibernate+Spring project, much of the functionality is based around the Inbox page where, using many filtering options (not all of them have to be used), users can restrict the set of objects they want to work with. I was wondering what the best strategy to implement this filtering is? In the old version of this application, the search query was built concatenating strings containing SQL conditions. Recently I read about the new Criteria API JPA provides - would you recommend this over working with the search string? And how does this combine with the DAO layer - isn't building the search query using Criteria API in the business layer a breach in separation of layers?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

李不 2024-10-05 14:41:54

对于像您所描述的过滤查询,我绝对建议使用 Hibernate 或 JPA criteria API,因为它们支持条件查询。我通常只是将标准构造代码放入 DAO 中,并在那里传递所有必需的(可能为空)参数。

以下是使用 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 中的联接的地方。

For filtering queries like you describe I definitely recommend using the Hibernate or JPA criteria API because of the support for conditional queries. I usually just put the criteria construction code in my DAO's and pass all the required (possibly null) arguments there.

Here's an example DAO method from an example car-rental application using the Hibernate criteria API:

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;
}

The createAlias call can be used where you would need a join in SQL.

酒解孤独 2024-10-05 14:41:54

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

添加到上面 DAO 层是为了访问数据而创建的,这一层应该像玻璃一样清晰,没有任何复杂的编码或业务逻辑,但是在条件的情况下,必须编写一个逻辑(创建条件)来得出一个结果更好且经过调整的访问对象的方式,因此在我看来,将这么多逻辑放入 DAO 层并没有违规。

even i will prefer using Criteria over HQL and SQL, for me reason will be modularity and also performance, because when the project comes into production, the major problem that we face is performance, neither HQL nor SQL can compete Criteria over performance.

Adding to above The DAO layer is created for accessing the data, and this layer should be as clear as glass without any complex coding or business logic, but in case of criteria, one has to write a logic(create criteria) to arrive at a better and tuned way to access object, So in my view there is no breach in putting this much logic in DAO layer.

胡大本事 2024-10-05 14:41:54

两种方法:

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

title:"The Right Way" & mod_date:[20020101 至 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 动态查找器

如果您确实想要完全分离层,您可以实现一个简单的语法。然后解析它以创建相关标准。这将允许改变基本标准的实施。这是否合适取决于这种抽象对您来说有多重要。

Two approaches:

1.. Depending on what kind of filtering you need you may be able to acheive this by searching e.g. index all the objects with Lucene and then use search queries to perform the filtering. e.g build up a query like:

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

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


2.. Or using criteria...

I'd use the new type-safe criteria api from hibernate:

http://relation.to/12805.lace

Rather than one method that builds up a very large criteria, I'd try to seperate out all the logic using detached criteria -

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

With a combination of these two you would be able to build up criteria easily.

One other place to look for inspiration is the grails dynamic finders. This is essentially what you are trying to achieve in a static way.

http://www.grails.org/doc/1.0.x/guide/single.html#5.4.1 Dynamic Finders

If you really want complete separation of layers you could implement a simple grammar. Then parse this to create the relevant criteria. This would allow for changing of the underlying criteria implementations. Whether this is appropriate depends on how crucial this abstraction is to you.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文