Я реализовал довольно похожее требование. Я использую Criteria API, потому что вам не нужно использовать строковые операции, чтобы собрать запрос, что делает его более стабильным.
Я опубликовал простой пример в этом вопросе , как составить динамический запрос.
В нашем решении я не разрешил операции OR, потому что это делает его очень сложным (также часть пользовательского интерфейса) и производительность может ухудшиться. Но позже мы, вероятно, также осуществим это.
Проектные решения должны приниматься в соответствии с вашими требованиями, сложностью ваших запросов и структурой данных.
Например, я создал класс фильтра для каждого запроса. Класс фильтра содержит предопределенные поля. Это делает его более стабильным, потому что вы всегда знаете, какие поля могут быть там, и можете поместить их в определенные места в запросе. Некоторые поля требуют подзапросов. В нашем случае невозможно сделать его полностью универсальным (это означает, что вся информация о том, как должен формироваться запрос, хранится в фильтре). Для каждого класса фильтров существует особый метод, позволяющий превратить его в запрос. Это дает вам большую гибкость.
Мои критерии состоят из ссылки на поле, оператора (enum) и аргумента.