使用Spring JPA规范进行多列搜索

时间:2017-10-27 08:29:38

标签: spring spring-boot spring-data-jpa jpql jpa-criteria

我想在Spring-Boot后端创建一个多字段搜索。如何使用Specification<T>

执行此操作

环境

Springboot
Hibernate
Gradle
Intellij

前端的UI是Jquery Datatable。每列允许应用单个字符串搜索词。多个列中的搜索字词由and加入。

enter image description here

我有来自前端的过滤器已经填充到Java对象中。

第1步 扩展JPA规范执行者

public interface SomeRepository extends JpaRepository<Some, Long>, PagingAndSortingRepository<Some, Long>, JpaSpecificationExecutor {

第二步 创建一个新类SomeSpec

这就是我迷失了代码的样子以及它是如何工作的。

每列需要一个方法吗? 什么是Root,什么是Criteria Builder? 还需要什么?

我是JPA的新人,所以虽然我不需要任何人为我编写代码,但详细解释会很好。

更新 看来QueryDSL是更简单,更好的方法。我正在使用Gradle。我是否需要从this更改build.gradle?

2 个答案:

答案 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));
}