【问题标题】:How to make an advanced search with Spring Data REST?如何使用 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

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

谢谢。

【问题讨论】:

  • 找到比您开始使用的方法更好的方法了吗?
  • 看看我的回答。
  • 我相信我已经完成了你想要的,请检查我的答案。

标签: java spring java-8 spring-data-rest


【解决方案1】:

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

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

【讨论】:

  • 不要认为query dsl可以和SDR结合起来。
  • @matr0s 看看QueryDslPredicateExecutor interfaceQuerydsl 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
      猜你喜欢
      • 2021-04-12
      • 2014-04-30
      • 1970-01-01
      • 2016-07-11
      • 2018-09-05
      • 1970-01-01
      • 2016-05-01
      • 2021-10-05
      • 1970-01-01
      相关资源
      最近更新 更多