JPA CriteriaBuilder - как добавить параметры с различными динамическими типами в критерии? Получение неоднозначной ошибки вызова - PullRequest
0 голосов
/ 03 октября 2019

Я новичок в JPA и застрял с довольно простым вариантом использования. Все, что я хочу, - это добавить некоторые условия в мои критерии на основе определенных пропусков пользователя фильтра в мое приложение. Пользователь передает информацию в приложение, используя пары ключ, значение, и мы расшифруем ее, чтобы увидеть, какой тип у каждого параметра.

Например:

1) ключ = id и значение = 20. Мы будем интерпретировать его как поиск по id = 20, где id - это целое число

2) ключ = имя и значение = 'тест'. Мы будем интерпретировать его как поиск по имени = 'test', где name - строка

Это достигается с помощью такого метода, как этот

public <T> T getSearchValue(Object inputValue) {
   if (condition) {
      return (T) inputValue.toString();
   } else {
      return (T) Integer.parseInt(inputValue.toString);
   }
}

Я думал, что мог бы легко добавитьусловие с использованием критериев застройщика. Примерно так

cb.equal(getSearchKey(), getSearchValue(inputValue));
cb.gt(getSearchKey(), getSearchValue(inputValue));

Здесь cb.equal отлично работает. Но cb.gt или любая другая операция, такая как lessThan, like и т. Д., Все дают ошибку компиляции, что есть 2 совпадения методов и, следовательно, неоднозначно

Первый метод gt(Expression<? extends Number>, Expression<? extends Number>), а второй gt(Expression<? extends Number>, Number)

Итак, как мне решить эту проблему и убедиться, что она может преобразоваться в число в качестве второго параметра, например? Проблема здесь в том, что я не знаю тип заранее, так как он добавляется в зависимости от того, с каким ключевым пользователем пользователь хочет выполнить поиск. Можно ли как-то динамически привести к классу, или здесь есть какой-то другой лучший подход?

1 Ответ

2 голосов
/ 03 октября 2019

Идея состоит в том, чтобы предоставить ограничения для предложения where в «операторе SQL».

SELECT * FROM table_name WHERE key=value

Многим людям нравится писать JPQL-запросы, похожие на SQL, потому что их легче понять при чтении. Тем не менее, есть некоторые преимущества использования этого Criteria API (динамический, меньше подвержен ошибкам, меньше внимания для безопасности, легче реорганизовать).

Первый шаг - получить CriteriaBuilder, а затем создать CriteriaQuery с этим. После этого вы можете определить, из какого класса вы хотите построить Root, и использовать его для получения столбцов. Также могут следовать объединения.

CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<YourClass> cq = cb.createQuery(YourClass.class);
Root<YourClass>  root = cq.from(YourClass.class);

В качестве следующего шага вы хотите определить предложение where с его ограничениями (предикатами). Как и в обычном операторе SQL, там может быть только один оператор. Если у вас есть несколько ограничений, их необходимо объединить с criteriaBuilder.and().

Существует несколько способов применения всего процесса. У меня есть один способ, как я это делаю (и некоторые другие тоже могут это делать). Обычно я создаю List со всеми ограничениями, которые я хочу использовать, и создаю из него массив, который затем объединяется с cb.and().

List<Predicate> predicates = new ArrayList<>();
predicates.add(cb.equal(root.get("key1"), "value1"));
predicates.add(cb.gt(root.get("key2"), 100));

cq.select(root).where(cb.and(predicates.toArray(new Predicate[predicates.size()])));

Ниже приведен целый пример такого DAO. method.

public List<Foo>(String state, int size, String column, String value) {
    CriteriaBuilder cb = entityManager.getCriteriaBuilder();
    CriteriaQuery<Foo> cq = cb.createQuery(Foo.class);
    Root<Foo>  root = cq.from(Foo.class);

    List<Predicate> predicates = new ArrayList<>();
    predicates.add(cb.equal(root.get(Foo_.bla), state));
    predicates.add(cb.gt(root.get(Foo_.blub), size));
    predicates.add(cb.equal(root.get(column), value));

    cq.select(root).where(cb.and(predicates.toArray(new Predicate[predicates.size()])));

    return entityManager.createQuery(cq).getResultList();
}

Для получения имен столбцов (или сингулярных атрибутов) я использовал hibernate-jpamodelgen. Посмотрите на этот плагин, который автоматически генерирует классы (Foo_), что делает его более безопасным при использовании имен столбцов.

Обновление: если вы не знаете имен столбцов для ограничений в предложении whereзаранее его просто отрегулировать (см. третье ограничение выше).

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