Вопрос или проблема
У меня есть набор значений фильтров, которые приходят в виде List<String>
. Они сопоставляются с varchar()[]
в базе данных postgres. Я пытаюсь создать
Я не понимаю, как сопоставить массивы базы данных и спецификации.
Не работающий кусок кода прикреплен
private Specification<DatabaseTable> hasFilters(
List<String> filters) {
return (root, query, cb) -> {
Predicate filterPredicate = cb.disjunction();
if (!CollectionUtils.isEmpty(filters)) {
for (String filter : filters) {
filterPredicate = cb.isNotNull(
cb.function("array_position", Integer.class, root.get("columnName"),
cb.literal(filter))
);
}
}
return filterPredicate;
};
}
Идея заключается в том, чтобы использовать функцию postgres ‘array_position’ для поиска строк внутри массива и проверки, есть ли у них позиция в массиве. И если значение имеет позицию, я бы сопоставил его с cb.isNotNull Но в конце концов это просто не работает, возвращенные значения не фильтруются. Любые идеи или предложения были бы очень полезны.
Ответ или решение
Для решения предусмотренной задачи с фильтрацией данных в PostgreSQL на основе типа данных varchar[]
, необходимо правильно сформировать спецификацию для обработки запросов с использованием библиотеки Spring Data JPA. Ваша текущая реализация имеет некоторые недостатки, которые не позволяют корректно фильтровать результаты.
Проблема
Вы пытаетесь использовать функцию array_position
, чтобы определить, имеется ли переданное значение в массиве. Однако ваша реализация зависит от неправильного применения cb.isNotNull
, что не даст ожидаемых результатов.
Решение
Ниже приведен корректный способ реализации спецификации, которая будет использовать cb.equal
для создания предиката,-соответствующего фильтрам.
import org.springframework.data.jpa.domain.Specification;
import javax.persistence.criteria.*;
private Specification<DatabaseTable> hasFilters(List<String> filters) {
return (root, query, cb) -> {
// Убедимся, что список фильтров не пустой
if (CollectionUtils.isEmpty(filters)) {
return cb.conjunction(); // Возвращаем истинное выражение, если фильтров нет
}
// Создаем предикат для сравнения массива
Predicate filterPredicate = cb.disjunction();
for (String filter : filters) {
// Используем функцию array_position для проверки наличия значения в массиве
Predicate condition = cb.gt(cb.function("array_position", Integer.class, root.get("columnName"), cb.literal(filter)), 0);
filterPredicate = cb.or(filterPredicate, condition); // Объединяем условия с использованием OR
}
return filterPredicate; // Возвращаем сформированный предикат
};
}
Объяснение решения
-
Использование
cb.gt
: Здесь мы проверяем, вернула ли функцияarray_position
значение больше нуля, что означает, что элемент найден в массиве. Это дает вам возможность фильтровать массивы таким образом, чтобы избежать ошибок null. -
Объединение условий: Мы используем
cb.or
, чтобы объединить все условия фильтра с помощью логического оператора OR, что позволяет вернуть записи, содержащие хотя бы одно из значений из списка фильтров. -
Проверка пустого списка: Если список фильтров пустой, возвращаем
cb.conjunction()
, что эквивалентно истинному выражению. -
Переход к функции
array_position
: Функция PostgreSQLarray_position
возвращает позицию элемента в массиве, и если она не находит элемент, возвращает null. Проверка на большее нуля гарантирует, что элемент действительно существует в массиве.
Вывод
При разработке спецификаций в JPA важно правильно понимать, как соответствовать элементам в базах данных. Использование функций PostgreSQL совместно с критериями JPA открывает вам большие возможности для создания сложных фильтров и запросов. Убедитесь в тестировании выхода маскированных значений на конечные результаты ваших запросов. Это поможет вам диагностировать любые проблемы и гарантирует, что спецификация будет работать так, как ожидается.