С помощью веб-поддержки Querydsl , входящей в веб-поддержку расширения данных Spring , мы можем легко реализовать своего рода «язык запросов REST API» для фильтрации наших сущностей.
Все, что нам нужно, это сделать следующее:
1) расширить наш репозиторий с QuerydslPredicateExecutor
,
2) добавить Predicate
с аннотацией @QuerydslPredicate
в качестве аргумента к нашему методу контроллера REST
3) использовать этот предикат в findAll
методе репозитория:
public interface PersonRepo extends JpaRepository<Person, Long>, QuerydslPredicateExecutor<Person> {
}
@RequiredArgsConstructor
@RestController
@RequestMapping("/people")
public class PersonController {
private final PersonRepo personRepo;
@GetMapping
public ResponseEntity getFiltered(@QuerydslPredicate(root = Person.class) Predicate predicate, Pageable pageable) {
return ResponseEntity.ok(personRepo.findAll(predicate, pageable)));
}
}
Тогда мы сможем запросить наши данные:
GET /people?name=John&age=18&page=1&sort=name,desc
Далее мы должны сделать нечувствительный к регистру фильтр «как». Для этого мы расширяем репо с QuerydslBinderCustomizer
и переопределяем его метод customize
(прямо в репо):
public interface PersonRepo extends
JpaRepository<Person, Long>,
QuerydslPredicateExecutor<Person>,
QuerydslBinderCustomizer<QPerson> {
@Override
default void customize(QuerydslBindings bindings, QPerson person) {
// Make case-insensitive 'like' filter for all string properties
bindings.bind(String.class).first((SingleValueBinding<StringPath, String>) StringExpression::containsIgnoreCase);
}
}
Чтобы это работало, мы должны добавить параметр bindings
к @QuerydslPredicate
нашего метода контроллера:
@GetMapping
public ResponseEntity getFiltered(
@QuerydslPredicate(root = Person.class, bindings = PersonRepo.class) Predicate predicate,
Pageable pageable
) {
return ResponseEntity.ok(personRepo.findAll(predicate, pageable)));
}
Теперь мы можем запросить наши данные в соответствии с вопросом:
GET /people?name=jo&age=18&page=1&sort=name,desc
С помощью QuerydslBinderCustomizer
мы можем реализовать более сложные фильтры, например, фильтры between
и greater or equal
(добавьте этот код в метод customize
):
bindings.bind(person.age).all((path, value) -> {
Iterator<? extends Integer> it = value.iterator();
Integer from = it.next();
if (value.size() >= 2) {
Integer to = it.next();
return Optional.of(path.between(from, to)); // between
} else {
return Optional.of(path.goe(from)); // greater or equal
}
});
Если в запросе указать два age
параметра, то мы получим все записи с возрастом между этими параметрами. Если мы укажем только один age
параметр - мы получим записи, возраст которых больше или равен этому значению.
GET /people?age=18&age=30
... получают все люди в возрасте от 18 до 30
GET /people?age=18
... получить всех людей, возраст которых больше или равен 18
В конце мы можем исключить некоторые ненужные свойства из фильтра, например, сущность id
(добавить этот код в метод customize
):
bindings.excluding(person.id);
Чтобы использовать веб-поддержку Querydsl, мы должны добавить эти зависимости и плагин в наш проект Spring Boot:
<dependencies>
<!-- ... -->
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-apt</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<!-- ... -->
<plugin>
<groupId>com.mysema.maven</groupId>
<artifactId>apt-maven-plugin</artifactId>
<version>1.1.3</version>
<executions>
<execution>
<goals>
<goal>process</goal>
</goals>
<configuration>
<outputDirectory>target/generated-sources/annotations</outputDirectory>
<processor>com.querydsl.apt.jpa.JPAAnnotationProcessor</processor>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
Затем важно, скомпилировать проект , чтобы построить "Q-классы" наших сущностей.
Полный пример демонстрации вы можете найти в моем репозитории: sb-querydsl-sd-demo и Почтальон API-документы этой демонстрации - здесь: Язык запросов REST с данными Querydsl и Spring .