Как преодолеть ограничения Hibernate Criteria и Example API? - PullRequest
6 голосов
/ 09 сентября 2010

Я нахожусь в положении, когда в нашей компании имеется служба поиска в базе данных с широкими возможностями настройки, для которой очень полезно настраивать запросы программным способом.API Criteria является мощным, но когда один из наших разработчиков реорганизует один из объектов данных, ограничения критериев не будут сигнализировать о том, что они нарушены, пока мы не запустим наши модульные тесты или, что еще хуже, живые и в нашей производственной среде.Недавно у нас был проект по рефакторингу, по сути, удвоенный в рабочем времени, неожиданно из-за этой проблемы, пробела в планировании проекта, который, если бы мы знали, сколько времени на самом деле это займет, мы, вероятно, выбрали бы альтернативный подход.

Я хотел бы использовать Пример API для решения этой проблемы.Компилятор Java может громко указать, что наши запросы не выполняются, если мы указываем условия «где» для реальных свойств POJO.Тем не менее, есть только так много функциональности в примере API, и это ограничивает во многих отношениях.Возьмите следующий пример

 Product product = new Product();
 product.setName("P%");
 Example prdExample = Example.create(product);
 prdExample.excludeProperty("price");
 prdExample.enableLike();
 prdExample.ignoreCase();

Здесь запрашивается свойство "name" (где name, например, "P%"), и если бы я удалил или переименовал поле "name", мы бызнать мгновенно.Но как насчет собственности "цена"?Это исключено, потому что объект Product имеет какое-то значение по умолчанию, поэтому мы передаем имя свойства price в фильтр исключения.Теперь, если «цена» будет удалена, этот запрос будет синтаксически недействительным, и вы не узнаете до времени выполнения.LAME.

Еще одна проблема - что если мы добавим второе предложение where:

 product.setPromo("Discounts up to 10%");

Из-за вызова enableLike () этот пример будет совпадать с промо-текстом «Скидки до10% », но также« Скидки до 10 000 000 долларов »или что-либо еще, что соответствует.В общем, изменения в объекте запроса для примера, такие как enableLike () или ignoreCase (), не всегда будут применимы к каждому проверяемому свойству.

Вот третий и главный вопрос -а как насчет других специальных критериев?Невозможно получить каждый продукт по цене, превышающей 10 долларов, используя стандартную платформу для примеров.Нет возможности упорядочить результаты по акции, по убыванию.Если объект Product объединяется с каким-либо производителем, нет никакого способа добавить критерий к связанному объекту производителя.Также невозможно безопасно указать FetchMode в критериях для производителя (хотя это проблема с API Criteria в целом - недопустимые извлеченные отношения завершаются сбоем молча, даже больше, чем бомба замедленного действия)

Для всехВ приведенных выше примерах вам нужно будет вернуться к API Criteria и использовать строковые представления свойств для выполнения запроса - опять же, исключая наибольшее преимущество запросов Example.

Какие существуют альтернативы API примера, которые могутполучить совет, который нам нужен во время компиляции?

Ответы [ 3 ]

5 голосов
/ 12 сентября 2010

Моя компания дает разработчикам дни, когда мы можем экспериментировать и работать над любимыми проектами (например, в Google), и я потратил некоторое время на работу над платформой для использования примеров запросов, обходя ограничения, описанные выше.Я придумал что-то, что могло бы быть полезным для других людей, интересующихся также примерами запросов.Вот пример структуры с использованием примера Product.

 Criteria criteriaQuery = session.createCriteria(Product.class);

 Restrictions<Product> restrictions = Restrictions.create(Product.class);
 Product example = restrictions.getQueryObject();
 example.setName(restrictions.like("N%"));
 example.setPromo("Discounts up to 10%");

 restrictions.addRestrictions(criteriaQuery);

Вот попытка исправить проблемы в примере кода из вопроса - проблема значения по умолчанию для поля "price" больше не существуетсуществует, потому что эта структура требует, чтобы критерии были установлены явно.Вторая проблема, связанная с включением функции enableLike () для всего запроса, устранена - средство сопоставления находится только в поле «имя».

Другие проблемы, упомянутые в вопросе, также исчезли в этой структуре.Вот примеры реализаций.

 product.setPrice(restrictions.gt(10)); // price > 10
 product.setPromo(restrictions.order(false)); // order by promo desc
 Restrictions<Manufacturer> manufacturerRestrictions 
        = Restrictions.create(Manufacturer.class);
 //configure manuf restrictions in the same manner...
 product.setManufacturer(restrictions.join(manufacturerRestrictions)); 
 /* there are also joinSet() and joinList() methods
  for one-to-many relationships as well */

Доступны даже более сложные ограничения.

 product.setPrice(restrictions.between(45,55));
 product.setManufacturer(restrictions.fetch(FetchMode.JOIN));
 product.setName(restrictions.or("Foo", "Bar"));

После показа структуры коллеге он упомянул, что многие объекты, сопоставленные с данными, имеют частные установщики, что делаетэтот тип критериев также сложен (другая проблема с примером API!).Итак, я тоже это учел.Вместо использования сеттеров, геттеры также могут запрашиваться.

 restrictions.is(product.getName()).eq("Foo");
 restrictions.is(product.getPrice()).gt(10);
 restrictions.is(product.getPromo()).order(false);

Я также добавил некоторые дополнительные проверки объектов для обеспечения лучшей безопасности типов - например, относительные критерии (gt, ge, le, lt).) все требуют значения?расширяет Comparable для параметра.Кроме того, если вы используете метод получения в указанном выше стиле, и на получателе присутствует аннотация @Transient, он выдаст ошибку времени выполнения.

Но подождите, есть еще!

Есливам нравится, что встроенная в Hibernate утилита Restrictions может быть статически импортирована, так что вы можете делать такие вещи, как attribute.addRestriction (eq ("name", "foo")), не делая ваш код действительно подробным, для этого тоже есть вариант.

 Restrictions<Product> restrictions = new Restrictions<Product>(){
       public void query(Product queryObject){
         queryObject.setPrice(gt(10));
         queryObject.setPromo(order(false));
         //gt() and order() inherited from Restrictions
       }            
 }

Вот и все - заранее большое спасибо за любые отзывы!Мы разместили код на Sourceforge для тех, кто заинтересован.http://sourceforge.net/projects/hqbe2/

1 голос
/ 30 декабря 2013

Посмотрите на Querydsl .Их модуль JPA / Hibernate требует генерации кода.Их модуль коллекций Java использует прокси, но в настоящее время его нельзя использовать с JPA / Hibernate.

1 голос
/ 13 сентября 2010

API выглядит великолепно!

Restrictions.order (булево) пахнет как управляющая связь.Немного неясно, что представляют значения логического аргумента.

Я предлагаю заменить или дополнить на orderAscending () и orderDescending ().

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...