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

问题描述 投票:0回答:2

如何使用Spring Data轻松实现一种“REST API查询语言”来过滤实体?

例如,对于以下

Person
实体:

@Data
@Entity
public class Person {

  @Id
  @GeneratedValue
  private Long id;

  private LocalDate dob; // date of birth

  private String name;

  @Formula("timestampdiff('year', dob, now())")
  private Integer age;

  public Person(String name, LocalDate dob) {
    this.name = name;
    this.dob = dob;
  }
}

我想通过这样的请求获取它的数据:

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

即:“获取所有

name
包含“jo”(不区分大小写)且
age
等于 18 的人的第一页,按
name
降序排序”。

spring rest spring-boot spring-data querydsl
2个回答
22
投票

借助Querydsl Web支持Web支持Spring Data扩展的部分,我们可以轻松实现一种“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

接下来我们必须设置不区分大小写的“like”过滤器。为此,我们从

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 Web 支持,我们必须将这些依赖项和插件添加到我们的 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,以及此演示的Postman API 文档 - 这里:带有 Querydsl 和 Spring Data 的 REST 查询语言


0
投票
QueryDSL Web 支持是一个不错的选择,但当涉及到更具体的领域时很难定制。 相反,您可以使用

rsql-querydsl

,它在支持的运算符和其他一些优势方面提供更多自由。

首先将库添加到你的POM中:

<dependency> <groupId>io.github.apulbere</groupId> <artifactId>rsql-querydsl</artifactId> <version>1.0</version> </dependency>
然后定义一个代表您的搜索条件的 DTO。请注意,这是 DTO,而不是实体本身,这有助于将持久层与视图分开:

@Setter @Getter public class PersonCriteria { StringCriteria name = StringCriteria.empty(); LongCriteria age = LongCriteria.empty(); }
最后构建谓词。请注意,您可以根据需要在搜索 DTO 和 Q 模型之间进行映射,这为 REST API 的外观提供了更大的灵活性:

@GetMapping("/people") List<PersonDTO> search(PersonCriteria criteria, Pageable page) { var predicate = criteria.age.match(QPerson.age) .and(criteria.name.match(QPerson.name)); return peopleRepository.findAll(predicate, page) .stream() .map(personMapper::map) .toList(); }
    
© www.soinside.com 2019 - 2024. All rights reserved.