Сложные поисковые запросы JPA - PullRequest
10 голосов
/ 22 октября 2010

В моем проекте Wicket + JPA / Hibernate + Spring большая часть функциональности основана на странице «Входящие», где, используя множество параметров фильтрации (не все из них должны использоваться), пользователи могут ограничить набор объектов, которые они хотят работать с. Мне было интересно, какова лучшая стратегия для реализации этой фильтрации? В старой версии этого приложения поисковый запрос был построен объединяющими строками, содержащими условия SQL. Недавно я прочитал о новых Criteria API, которые предоставляет JPA - вы бы порекомендовали это по сравнению со строкой поиска? И как это сочетается со слоем DAO - не является ли создание поискового запроса с использованием Criteria API на бизнес-уровне нарушением разделения слоев?

Ответы [ 3 ]

4 голосов
/ 25 октября 2010

Для фильтрации запросов, которые вы описываете, я определенно рекомендую использовать API критериев Hibernate или JPA из-за поддержки условных запросов.Обычно я просто помещаю код построения критериев в свои DAO и передаю туда все необходимые (возможно, нулевые) аргументы.

Вот пример метода DAO из примера приложения по прокату автомобилей с использованием API критериев Hibernate:

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 голос
/ 31 октября 2010

Два подхода:

1 .. В зависимости от того, какой тип фильтрации вам необходим, вы можете достичь этого, выполнив поиск, например, индексирования всех объектов с помощью Lucene, а затем используйте поисковые запросы для выполнения фильтрации.например, создать запрос типа:

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

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


2.Или используя критерии ...

Я бы использовал новые безопасные для типов критерии api из hibernate:

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 Динамические искатели

Если вы действительно хотите полное разделение слоев, вы можете реализовать простую грамматику.Затем проанализируйте это, чтобы создать соответствующие критерии.Это позволит изменить базовые критерии реализации.Уместно ли это, зависит от того, насколько важна для вас эта абстракция.

1 голос
/ 27 октября 2010

даже если я предпочитаю использовать критерии по сравнению с HQL и SQL, для меня причина будет в модульности, а также в производительности, потому что, когда проект вступает в эксплуатацию, основная проблема, с которой мы сталкиваемся, это производительность, ни HQL, ни SQL не могут конкурировать с критериями по производительности. ,

Добавление к вышеперечисленному Уровень DAO создан для доступа к данным, и этот уровень должен быть прозрачным, как стекло, без какого-либо сложного кодирования или бизнес-логики, но в случае критериев необходимо написать логику (создать критерии) для придумать лучший и настроенный способ доступа к объекту, так что, на мой взгляд, нет никакого нарушения в размещении такого большого количества логики на уровне DAO.

...