Если вы хотите создать свой особенный фильтр, я думаю, вам следует начать с изобретения вашего поискового интерфейса. Например, вот так:
GET /models?name=eq(john smith)&createdAt=between(2019-01-01,2019-01-31)
GET /models?name=like(sm)&createdAt=from(2019-01-01)
GET /models?name=sw(john)&createdAt=to(2019-01-31)
После этого вы сможете попробовать это реализовать.
IMO лучший способ решить такую задачу - использовать Spring Data JPA Спецификации (и JPA Criteria API). Например:
1) Давайте создадим класс Filter
, который реализует Specification
для нашей сущности Model
:
@Value
public class ModelFilter implements Specification<Model> {
private String name;
private String createdAt;
@Override
public Predicate toPredicate(Root<Model> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
List<Predicate> predicates = new ArrayList<>();
// Prepare predicates and fill the list with them...
return builder.and(predicates.toArray(new Predicate[0]));
}
}
2) Затем создайте метод контроллера:
@GetMapping
public List<Model> getAllByFilter(ModelFilter filter) {
return repo.findAll(filter);
}
Осталось только подготовить наши предикаты))
Чтобы сделать это, мы можем сначала создать удобный интерфейс «Predicate Builder»:
@FunctionalInterface
interface PredicateBuilder<T> {
Optional<Predicate> get(String fieldName, String value, Root<T> root, CriteriaBuilder builder);
static Matcher getMatcher(String op, String value) {
return getMatcher(op, value, "(.+)");
}
static Matcher getMatcher(String op, String value, String pattern) {
return Pattern.compile(op + "\\(" + pattern + "\\)").matcher(value);
}
}
Тогда попробуйте сделать наши предикаты:
равна
PredicateBuilder<Model> eq = (fieldName, value, root, cb) -> {
Matcher m = getMatcher("eq", value);
if (m.matches()) {
return Optional.of(cb.equal(cb.upper(root.get(fieldName)), m.group(1).toUpperCase()));
} else {
return Optional.empty();
}
};
как
PredicateBuilder<Model> like = (fn, value, root, cb) -> {
Matcher m = getMatcher("like", value);
if (m.matches()) {
return Optional.of(cb.like(cb.upper(root.get(fn)), "%" + m.group(1).toUpperCase() + "%"));
} else {
return Optional.empty();
}
};
начать с
PredicateBuilder<Model> sw = (fn, value, root, cb) -> {
Matcher m = getMatcher("sw", value);
if (m.matches()) {
return Optional.of(cb.like(cb.upper(root.get(fn)), m.group(1).toUpperCase() + "%"));
} else {
return Optional.empty();
}
};
между
PredicateBuilder<Model> between = (fn, value, root, cb) -> {
Matcher m = getMatcher("between", value, "(.+)\\s*,\\s*(.+)");
if (m.matches()) {
LocalDate from = LocalDate.parse(m.group(1));
LocalDate to = LocalDate.parse(m.group(2));
return Optional.of(cb.between(root.get(fn), from, to));
} else {
return Optional.empty();
}
};
от
PredicateBuilder<Model> from = (fn, value, root, cb) -> {
Matcher m = getMatcher("from", value);
if (m.matches()) {
LocalDate from = LocalDate.parse(m.group(1));
return Optional.of(cb.greaterThanOrEqualTo(root.get(fn), from));
} else {
return Optional.empty();
}
};
до
PredicateBuilder<Model> to = (fn, value, root, cb) -> {
Matcher m = getMatcher("to", value);
if (m.matches()) {
LocalDate to = LocalDate.parse(m.group(1));
return Optional.of(cb.lessThanOrEqualTo(root.get(fn), to));
} else {
return Optional.empty();
}
};
Осталось только завершить Filter
класс:
@Value
public class ModelFilter implements Specification<Model> {
private String name;
private String createdAt;
PredicateBuilder<Model> eq = ... ;
PredicateBuilder<Model> like = ... ;
PredicateBuilder<Model> sw = ... ;
PredicateBuilder<Model> between = ... ;
PredicateBuilder<Model> from = ... ;
PredicateBuilder<Model> to = ... ;
@Override
public Predicate toPredicate(Root<Model> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
List<Predicate> predicates = new ArrayList<>();
if (name != null) {
eq.get("name", name, root, builder).ifPresent(predicates::add);
like.get("name", name, root, builder).ifPresent(predicates::add);
sw.get("name", name, root, builder).ifPresent(predicates::add);
}
if (createdAt != null) {
between.get("createdAt", createdAt, root, builder).ifPresent(predicates::add);
from.get("createdAt", createdAt, root, builder).ifPresent(predicates::add);
to.get("createdAt", createdAt, root, builder).ifPresent(predicates::add);
}
return builder.and(predicates.toArray(new Predicate[0]));
}
}
Конечно, это только один пример реализации. Вы можете создать свою собственную реализацию спецификаций и предикатов, которые вам нужны. Основные вещи здесь:
- придумайте свой интерфейс поиска
- сделать свой «фильтр» Спецификация
- подготовьте все необходимые предикаты
- используйте спецификацию фильтра в методе вашего контроллера