且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

如何使用 Querydsl 和 Spring Data 轻松实现“REST API 查询语言"来过滤实体?

更新时间:2022-01-29 00:14:46

借助Querydsl Web SupportWeb 支持 Spring Data 扩展,我们可以轻松实现一种REST API 查询语言"来过滤我们的实体.

With help of Querydsl Web Support, the part of Web support Spring Data extension, we can easy implement a kind of 'REST API query language' to filter our entities.

我们只需要做到以下几点:

All we need is do the following:

1) 从 QuerydslPredicateExecutor 扩展我们的存储库,

1) extend our repository from QuerydslPredicateExecutor,

2) 将带有注释 @QuerydslPredicatePredicate 作为参数添加到我们的 REST 控制器方法中

2) add Predicate with annotation @QuerydslPredicate, as argument, to our REST controller method

3) 在存储库的 findAll 方法中使用此谓词:

3) use this predicate in findAll method of the repository:

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)));
    }
}

然后我们就可以请求我们的数据了:

Then we will be able to request our data:

GET /people?name=John&age=18&page=1&sort=name,desc

接下来我们必须制作不区分大小写的like"过滤器.为此,我们从 QuerydslBinderCustomizer 扩展我们的 repo 并覆盖它的 customize 方法(就在 repo 中):

Next we have to make case insensitive 'like' filter. To do this we extend our repo from QuerydslBinderCustomizer and override its customize method (right in the repo):

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 中:

To make it works we have to add parameter bindings to @QuerydslPredicate of our controller method:

@GetMapping
public ResponseEntity getFiltered(
    @QuerydslPredicate(root = Person.class, bindings = PersonRepo.class) Predicate predicate, 
    Pageable pageable
) {
    return ResponseEntity.ok(personRepo.findAll(predicate, pageable)));
}

现在我们可以按照问题中的要求请求我们的数据:

Now we can request our data as asked in the question:

GET /people?name=jo&age=18&page=1&sort=name,desc

使用 QuerydslBinderCustomizer 我们可以实现更复杂的过滤器,例如 betweengreater or equal 过滤器(将此代码添加到 customize 方法):

With QuerydslBinderCustomizer we can implement more complex filters, for example between and greater or equal filters (add this code to customize method):

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 参数 - 我们会得到年龄大于或等于该值的记录.

If we specify two age parameters in the request then we get all records with the age between these parameters. If we specify only one age parameter - we get records with the age is greater or equal that value.

GET /people?age=18&age=30

...获取所有年龄在 18 到 30 岁之间的人

GET /people?age=18

...获取所有年龄大于或等于 18 岁的人

最后我们可以从过滤器中排除一些不必要的属性,例如实体id(将此代码添加到customize方法中):

In the end we can exclude some unnecessary properties from the filter, for example the entity id (add this code to customize method):

bindings.excluding(person.id);

要使用 Querydsl Web Support,我们必须将这些依赖项和插件添加到我们的 Spring Boot 项目中:

To use Querydsl Web Support we have to add these dependencies and plugin to our Spring Boot project:

<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-classes".

Then, it's important, compile the project to build 'Q-classes' of our entities.

您可以在我的 repo 中找到完整的示例演示:sb-querydsl-sd-demoPostman 此演示的 API 文档 - 此处:带有 Querydsl 和 Spring Data 的 REST 查询语言.

Full example demo you can find in my repo: sb-querydsl-sd-demo, and Postman API-docs of this demo - here: REST query language with Querydsl and Spring Data.