我想在Spring-Boot后端创建一个多字段搜索。如何使用Specification<T>
?
环境
Springboot
Hibernate
Gradle
Intellij
前端的UI是Jquery Datatable。每列允许应用单个字符串搜索词。多个列中的搜索字词由and
加入。
我有来自前端的过滤器已经填充到Java对象中。
第1步 扩展JPA规范执行者
public interface SomeRepository extends JpaRepository<Some, Long>, PagingAndSortingRepository<Some, Long>, JpaSpecificationExecutor {
第二步 创建一个新类SomeSpec
这就是我迷失了代码的样子以及它是如何工作的。
每列需要一个方法吗? 什么是Root,什么是Criteria Builder? 还需要什么?
我是JPA的新人,所以虽然我不需要任何人为我编写代码,但详细解释会很好。
更新 看来QueryDSL是更简单,更好的方法。我正在使用Gradle。我是否需要从this更改build.gradle?
答案 0 :(得分:2)
您可以考虑使用Spring Data对QueryDSL的支持,因为您可以在不必编写很多代码的情况下获得相当多的支持,即您实际上不必编写详细信息。
请点击此处查看概述:
https://spring.io/blog/2011/04/26/advanced-spring-data-jpa-specifications-and-querydsl/
基本上你的存储库变成了:
public interface SomeRepository extends JpaRepository<Some, Long>,
PagingAndSortingRepository<Some, Long>, QueryDslPredicateExecutor<Some>{
}
您还可以获取自动绑定到Controller中谓词的请求参数:
见这里:
https://spring.io/blog/2015/09/04/what-s-new-in-spring-data-release-gosling#querydsl-web-support
所以您的控制器看起来像:
@Controller
class SomeController {
private final SomeRepository repository;
@RequestMapping(value = "/", method = RequestMethod.GET)
String index(Model model,
@QuerydslPredicate(root = Some.class) Predicate predicate,
Pageable pageable) {
model.addAttribute("data", repository.findAll(predicate, pageable));
return "index";
}
}
因此,基于上述情况,它只是在项目中启用QueryDSL的情况,现在UI应该能够通过各种标准组合来过滤,排序和分页数据。
答案 1 :(得分:2)
如果您不想使用QueryDSL,则必须编写自己的规范。首先,您需要像{J}一样从JpaSpecificationExecutor
扩展您的存储库。确保添加通用(JpaSpecificationExecutor<Some>
)。
之后,您必须创建三个规范(每列一个),在the Spring docs中,他们将这些规范定义为类中的静态方法。基本上,创建规范意味着您必须子类化Specification<Some>
,它只有一种方法可以实现toPredicate(Root<Some>, CriteriaQuery<?>, CriteriaBuilder)
。
如果您正在使用Java 8,则可以使用lambdas创建匿名内部类,例如:
public class SomeSpecs {
public static Specification<Some> withAddress(String address) {
return (root, query, builder) -> {
// ...
};
}
}
对于实际实现,您可以使用Root
来访问特定节点,例如。 root.get("address")
。另一方面,CriteriaBuilder
是定义where子句,例如。 builder.equal(..., ...)
。
在你的情况下,你想要这样的东西:
public class SomeSpecs {
public static Specification<Some> withAddress(String address) {
return (root, query, builder) -> builder.equal(root.get("address"), address);
}
}
或者,如果您想使用LIKE
查询,可以使用:
public class SomeSpecs {
public static Specification<Some> withAddress(String address) {
return (root, query, builder) -> builder.like(root.get("address"), "%" + address + "%");
}
}
现在,您必须为要过滤的其他字段重复此操作。之后,您必须一起使用所有规范(使用and()
,or()
,...)。然后,您可以使用repository.findAll(Specification)
方法根据该规范进行查询,例如:
public List<Some> getSome(String address, String name, Date date) {
return repository.findAll(where(withAddress(address))
.and(withName(name))
.and(withDate(date));
}
您可以使用静态导入导入withAddress()
,withName()
和withDate()
,以便于阅读。 where()
方法也可以静态导入(来自Specification.where()
)。
请注意,上述方法可能需要进行调整,因为您不希望在地址栏中过滤null
。您可以通过返回null
来执行此操作,例如:
public List<Some> getSome(String address, String name, Date date) {
return repository.findAll(where(address == null ? null : withAddress(address))
.and(name == null ? null : withName(name))
.and(date == null ? null : withDate(date));
}