Комментарии 22
Можно ж в SQL написать .... ((:val IS NULL) OR (column = :val)).... В функцию передаем или null или значение.
Можно, конечно, но, если параметров станет не 3, а например, как в серьезном интернет-магазине, под пол сотни штук, то может получиться огромный сложный запрос с кучей джойнов и операторов.
ИМХО: Проще будет найти и внести изменения в java код, чем в огромный sql-запрос.
А чем CriteriaApi и Specification лучше данного подхода? Я, конечно понимаю, что мы все условия заворачиваем в спеки и основной сервис худеет засчет сервиса спецификаций.
Но по сути, это ровным счетом такая же дичь с теми же if-else, только реализация через еще более громоздкий синтаксис и без половины возможностей обычного SQL.
Имхо, hibernate вообще крайне неудачный выбор если мы хотим строить динамические запросы.
В таких случаях мы либо идем в сторону нативного SQL и StringBuilder, либо выбираем что-нибудь попроще. Например EBean.
Можно, мы так и делаем. Во всяких отчетах, где нет возможности оперировать запросом, только так.
Но такой стиль плохо на производительность влияет. Впрочем, в универсальных запросах с условиями по разным полям с производительностью и так все плохо.
В postgres, например, при таком запросе не отрабатывает индекс, насколько я помню, и будет всегда полный перебор
Использование StringBuilder для построения динамических SQL запросов больше похоже на антипаттерн, потому что:
Это сложно поддерживать, читать и отлаживать. Если у вас изменится какое-либо поле или название параметра - все эти изменения необходимо будет отслеживать и править вручную. И если вы это упустите, то заметите в лучшем случае, только во время интеграционных тестов (если они есть =)) или что хуже - это выстрелит только на проде. При использовании того же Criteria API с Hibernate Static Metamodel Generator, все ошибки вы увидите уже во время компиляции.
Отсутствие проверки типов данных. Можно сказать что тоже относится к первому пункту.
Когда вы собираете строки вручную есть потенциальная вероятность SQL инъекций.
Если вам не нравится Criteria API и вы хотите работать со строками, вы можете использовать тот же JOOQ. На самом же деле есть множество готовых решений для построения динамических SQL запросов: Criteria API, Querydsl, jOOQ, MyBatis и т.д.
Честно говоря, все эти решения имеют те или иные проблемы. Ну или ограничения, если хотите. Например, решения типа JOOQ предполагают, что у вас при компиляции структура базы такая же, как в проме. С одной стороны, вроде бы это плюс (вы примерно об этом пишете в п.1), а с другой - это далеко не всегда просто обеспечить. Просто потому, что база с которой вы работаете, может быть не ваша, и изменения в ее схему вносятся другой командой. Ну или вы просто заранее ничего не знаете про базу вообще, кроме того что она существует, и всю ее структуру можете добыть только при запуске, но не ранее.
Кажется для этих целей в JPA существует интерфейс Specification
А как же быть с индексами? Необходимо прояснить этот вопрос.
Простите, но это адская дичь. Есть же Specification. Берем CriteriaBuilder, берем входную DTO-шку и формируем при помощи и на основе их нормальный, красивый запрос.
Интересно, конечно, но чем Вам не угодили JPA спецификации?
Есть ощущение что мало людей знает про Specification. Недавно только делал подобное на спеках, возможно стоит пнуть себя и написать статью.
Два замечания. Нативный запрос (если я не ошибаюсь) выдает результат без помещения объектов в контекст, что может давать проблемы при использовании этих запросов в одной транзакции с нормальными jpa jpql запросами. Это надо учитывать.
Второе, хотелось бы подробное исследование по универсальным запросам - как сделать так, чтобы универсальные запросы по разным полями адекватно использовали индексы?
Народ, кто-нибудь сталкивался с Specification + Projection? По фильтру нужно получить не саму сущность, а её проекцию
Spring Data JPA и Hibernate: решаем проблему динамически изменяемого запроса к базам данных