如何使用 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) ") PageSpring 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<Person> findDistinctPeopleByLastnameOrFirstname(@Param("lastName")String lastname, @Param("firstName")String firstname);
和examples/search/searchByLastnameOrFirstname?firstName=value1&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以上是关于如何使用 Spring Data REST 进行高级搜索?的主要内容,如果未能解决你的问题,请参考以下文章
spring-data-rest 和控制器,使用相同的 objectMaper 进行序列化/反序列化
使用@RestController在Spring中进行分页,但不使用Spring Data Rest Pageable?