如何使用 Spring Data REST 进行高级搜索?

Posted

技术标签:

【中文标题】如何使用 Spring Data REST 进行高级搜索?【英文标题】:How to make an advanced search with Spring Data REST? 【发布时间】:2016-07-13 09:07:21 【问题描述】:

我的任务是使用 Spring Data REST 进行高级搜索。 如何实现?

我设法制作了一种方法来进行简单的搜索,比如这个:

public interface ExampleRepository extends CrudRepository<Example, UUID>

    @RestResource(path="searchByName", rel="searchByName")
    Example findByExampleName(@Param("example") String exampleName);


如果我只需要访问 url,这个例子就完美了:

.../api/examples/search/searchByName?example=myExample

但是如果要搜索的字段不止一个,我该怎么办?

例如,如果我的 Example 类有 5 个字段,我应该使用什么实现来对所有可能的字段进行高级搜索?

考虑这个:

.../api/examples/search/searchByName?filed1=value1&field2=value2&field4=value4

还有这个:

.../api/examples/search/searchByName?filed1=value1&field3=value3

我必须做些什么才能以适当的方式实现此搜索?

谢谢。

【问题讨论】:

看这里spring.io/blog/2011/04/26/… 找到比您开始使用的方法更好的方法了吗? 看看我的回答。 我相信我已经完成了你想要的,请检查我的答案。 【参考方案1】:

查询方法的实现在Springreferencedocumentation 和大量技术博客中有广泛的记录,尽管有很多已经过时了。

由于您的问题可能是“如何在不声明大量 findBy* 方法的情况下使用任意字段组合执行多参数搜索?”,所以答案是 Querydsl,由 Spring 支持。

【讨论】:

不要认为query dsl可以和SDR结合起来。 @matr0s 看看QueryDslPredicateExecutor interface。 Querydsl support in Spring 自 Gosling 发布 Spring 以来一直存在。我已经使用它几个月了,我可以谦虚地保证它;) 从来不知道查询 dsl 已集成到 SDR。非常感谢这个链接。【参考方案2】:

我设法使用Query by Example 实现了这一点。

假设您有以下模型:

@Entity
public class Company 

  @Id
  @GeneratedValue
  Long id;

  String name;
  String address;

  @ManyToOne
  Department department;




@Entity
public class Department 

  @Id
  @GeneratedValue
  Long id;

  String name;


还有存储库:

@RepositoryRestResource
public interface CompanyRepository extends JpaRepository<Company, Long> 

(注意JpaRepository 实现了QueryByExampleExecutor)。

现在你实现了一个自定义控制器:

@RepositoryRestController
@RequiredArgsConstructor
public class CompanyCustomController 

  private final CompanyRepository repository;

  @GetMapping("/companies/filter")
  public ResponseEntity<?> filter(
      Company company,
      Pageable page,
      PagedResourcesAssembler assembler,
      PersistentEntityResourceAssembler entityAssembler
  )

    ExampleMatcher matcher = ExampleMatcher.matching()
        .withIgnoreCase()
        .withStringMatcher(ExampleMatcher.StringMatcher.CONTAINING);

    Example example = Example.of(company, matcher);

    Page<?> result = this.repository.findAll(example, page);

    return ResponseEntity.ok(assembler.toResource(result, entityAssembler));

  

然后您可以进行如下查询:

localhost:8080/companies/filter?name=google&address=NY

您甚至可以查询嵌套实体,例如:

localhost:8080/companies/filter?name=google&department.name=finances

为简洁起见,我省略了一些细节,但我在 Github 上创建了一个 working example。

【讨论】:

【参考方案3】:

我已经为这项任务找到了一个可行的解决方案。

@RepositoryRestResource(excerptProjection=MyProjection.class)
public interface MyRepository extends Repository<Entity, UUID> 

    @Query("select e from Entity e "
          + "where (:field1='' or e.field1=:field1) "
          + "and (:field2='' or e.field2=:field2) "
          // ...
          + "and (:fieldN='' or e.fieldN=:fieldN)"
    Page<Entity> advancedSearch(@Param("field1") String field1,
                               @Param("field2") String field2,
                               @Param("fieldN") String fieldN,
                               Pageable page);


有了这个解决方案,使用这个基本网址:

http://localhost:8080/api/examples/search/advancedSearch

我们可以使用我们需要的所有字段进行高级搜索。

一些例子:

http://localhost:8080/api/examples/search/advancedSearch?field1=example
    // filters only for the field1 valorized to "example"

http://localhost:8080/api/examples/search/advancedSearch?field1=name&field2=surname
    // filters for all records with field1 valorized to "name" and with field2 valorized to "surname"

【讨论】:

编译出错,修改如下。 @Query("select e from Entity e " +" where (:field1='' or e.field1=:field1) " +"and (:field2='' or e.field2=:field2) " //.. . +"and (:fieldN='' or e.fieldN=:fieldN) ") Page advancedSearch(@Param("field1") String field1, @Param("field2") String field2, @Param(" fieldN") String fieldN, Pageable page);【参考方案4】:

Spring Data Rest 将 QueryDSL 与 Web 支持集成在一起,您可以将其用于您的高级搜索需求。您需要更改您的存储库以实现 QueryDslPredicateExecutor,然后一切就可以开箱即用了。

这是来自blog 文章中关于该功能的示例:

$ http :8080/api/stores?address.city=York    

    "_embedded": 
        "stores": [
            
                "_links": 
                    …
                , 
                "address": 
                    "city": "New York", 
                    "location":  "x": -73.938421, "y": 40.851 , 
                    "street": "803 W 181st St", 
                    "zip": "10033-4516"
                , 
                "name": "Washington Hgts/181st St"
            , 
            
                "_links": 
                    …
                , 
                "address": 
                    "city": "New York", 
                    "location":  "x": -73.939822, "y": 40.84135 , 
                    "street": "4001 Broadway", 
                    "zip": "10032-1508"
                , 
                "name": "168th & Broadway"
            , 
            …
        ]
    , 
    "_links": 
        …
    , 
    "page": 
        "number": 0, 
        "size": 20, 
        "totalElements": 209, 
        "totalPages": 11
    

【讨论】:

【参考方案5】:

我猜你可以尝试以下:

List&lt;Person&gt; findDistinctPeopleByLastnameOrFirstname(@Param("lastName")String lastname, @Param("firstName")String firstname);

examples/search/searchByLastnameOrFirstname?firstName=value1&amp;lastName=value2

查看:http://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.query-methods.query-creation

【讨论】:

是的,这是我在之前的回答中指出的链接之一。最多两个参数,这还可以,但是三个或更多,定义所有的findBy方法就有点尴尬了。如果您的搜索字段是“A”、“B”和“C”,您很快就会得到:findByA、findByB、findByC、findByAAndBAndC、findByAAndB、findByAAndC、findByBAndC... 现在想象 A、B、C 是有意义的变量名称,例如“名称、描述、创建日期”。 现在我正在尝试这样的事情:personRepo.findAll(new PersonAdvancedSearch(name, surname, age, etc...)),其中 PersonAdvancedSearch 实现了 Specification。 PersonAdvancedSearch 覆盖了 toPredicate 方法。它似乎有效,但如果属性是复杂类型,我仍然会遇到问题。 @AlessandroC 可能使用 @Query 注释手动制作您的方法可以帮助您更好地处理对域对象的搜索。 好吧,即使它是这样的。您的资源和存储库属于一类。除了在存储库接口中定义方法和...之外,您不需要编写单行sql

以上是关于如何使用 Spring Data REST 进行高级搜索?的主要内容,如果未能解决你的问题,请参考以下文章

spring-data-rest 和控制器,使用相同的 objectMaper 进行序列化/反序列化

Spring Data REST不完全指南

使用@RestController在Spring中进行分页,但不使用Spring Data Rest Pageable?

Spring Data REST

如何在 Spring-Data-Rest 中实现细粒度的访问控制?

如何禁用 Spring Data REST 存储库的默认公开?