Комплексные поисковые запросы JPA

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

Ответ 1

Для фильтрации запросов, подобных описанию, я определенно рекомендую использовать 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.

Ответ 2

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

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

Ответ 3

Два подхода:

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

title: "Правильный путь" и 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

С комбинацией этих двух вы сможете легко создавать критерии.

Другим местом для поиска вдохновения являются динамические искатели грааля. Это по существу то, чего вы пытаетесь достичь статическим образом.

http://www.grails.org/doc/1.0.x/guide/single.html#5.4.1 Динамические поисковые системы

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