简体   繁体   English

使用 Spring JPA 规范的多列搜索

[英]Multi-Column Search with Spring JPA Specifications

I want to create a multi field search in a Spring-Boot back-end.我想在 Spring-Boot 后端创建一个多字段搜索。 How to do this with a Specification<T> ?如何使用Specification<T>做到这一点?

Environment环境

Springboot
Hibernate
Gradle
Intellij

The UI in the front end is a Jquery Datatable.前端的 UI 是一个 Jquery Datatable。 Each column allows a single string search term to be applied.每列允许应用单个字符串搜索词。 The search terms across more than one column is joined by a and .跨多列的搜索词由and

在此处输入图片说明

I have the filters coming from the front end already getting populated into a Java object.我已经将来自前端的过滤器填充到 Java 对象中。

Step 1 Extend JPA Specification executor步骤 1扩展 JPA 规范执行器

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

Step2 Create a new class SomeSpec Step2创建一个新类 SomeSpec

This is where I am lost as to what the code looks like it and how it works.这就是我对代码的外观及其工作方式感到迷茫的地方。

Do I need a method for each column?我需要为每一列设置一个方法吗? What is Root and what is Criteria Builder?什么是 Root,什么是 Criteria Builder? What else is required?还需要什么?

I am rather new at JPA so while I don't need anyone to write the code for me a detailed explanation would be good.我是 JPA 的新手,所以虽然我不需要任何人为我编写代码,但详细的解释会很好。

UPDATE It appears QueryDSL is the easier and better way to approach this.更新QueryDSL 似乎是解决这个问题的更简单、更好的方法。 I am using Gradle.我正在使用 Gradle。 Do I need to change my build.gradle from this ?我需要我的build.gradle从改变这个

You could consider using Spring Data's support for QueryDSL as you would get quite a lot without having to write very much code ie you would not actually have to write the specifictions.您可以考虑使用 Spring Data 对 QueryDSL 的支持,因为您无需编写大量代码即可获得很多好处,即您实际上不必编写规范。

See here for an overview:请参阅此处了解概述:

https://spring.io/blog/2011/04/26/advanced-spring-data-jpa-specifications-and-querydsl/ https://spring.io/blog/2011/04/26/advanced-spring-data-jpa-specifications-and-querydsl/

Although this approach is really convenient (you don't even have to write a single line of implementation code to get the queries executed) it has two drawbacks: first, the number of query methods might grow for larger applications because of - and that's the second point - the queries define a fixed set of criterias .尽管这种方法真的很方便(您甚至不必编写一行实现代码来执行查询),但它有两个缺点:首先,对于较大的应用程序,查询方法的数量可能会增加,因为 - 这就是第二点 -查询定义了一组固定的标准 To avoid these two drawbacks, wouldn't it be cool if you could come up with a set of atomic predicates that you could combine dynamically to build your query?为了避免这两个缺点,如果您能提出一组可以动态组合以构建查询的原子谓词,岂不是很酷?

So essentially your repository becomes:所以基本上你的存储库变成:

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

}

You can also get request parameters automatically bound to a predicate in your Controller:您还可以获取自动绑定到控制器中的谓词的请求参数:

See here:看这里:

https://spring.io/blog/2015/09/04/what-s-new-in-spring-data-release-gosling#querydsl-web-support https://spring.io/blog/2015/09/04/what-s-new-in-spring-data-release-gosling#querydsl-web-support

SO your Controller would look like:所以你的控制器看起来像:

  @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";
    }
  }

So with the above in place it is simply a Case of enabling QueryDSL on your project and the UI should now be able to filter, sort and page data by various combinations of criteria.因此,有了上述内容,它只是在您的项目中启用 QueryDSL 的一个案例,并且 UI 现在应该能够通过各种条件组合过滤、排序和分页数据。

If you don't want to use QueryDSL, you'll have to write your own specifications.如果您不想使用 QueryDSL,则必须编写自己的规范。 First of all, you need to extend your repository from JpaSpecificationExecutor like you did.首先,您需要像您一样从JpaSpecificationExecutor扩展您的存储库。 Make sure to add the generic though ( JpaSpecificationExecutor<Some> ).确保添加泛型( JpaSpecificationExecutor<Some> )。

After that you'll have to create three specifications (one for each column), in the Spring docs they define these specifications as static methods in a class.之后,您必须创建三个规范(每列一个),在Spring 文档中,他们将这些规范定义为类中的静态方法。 Basically, creating a specification means that you'll have to subclass Specification<Some> , which has only one method to implement, toPredicate(Root<Some>, CriteriaQuery<?>, CriteriaBuilder) .基本上,创建规范意味着您必须toPredicate(Root<Some>, CriteriaQuery<?>, CriteriaBuilder) Specification<Some> ,它只有一个方法要实现, toPredicate(Root<Some>, CriteriaQuery<?>, CriteriaBuilder)

If you're using Java 8, you can use lambdas to create an anonymous inner class, eg.:如果您使用的是 Java 8,则可以使用 lambdas 创建匿名内部类,例如:

 public class SomeSpecs {
     public static Specification<Some> withAddress(String address) {
          return (root, query, builder) -> {
               // ...
          };
     }
 }

For the actual implementation, you can use Root to get to a specific node, eg.对于实际实现,您可以使用Root访问特定节点,例如。 root.get("address") . root.get("address") The CriteriaBuilder on the other hand is to define the where clause, eg.另一方面, CriteriaBuilder是定义 where 子句,例如。 builder.equal(..., ...) . builder.equal(..., ...)

In your case you want something like this:在你的情况下,你想要这样的东西:

 public class SomeSpecs {
     public static Specification<Some> withAddress(String address) {
          return (root, query, builder) -> builder.equal(root.get("address"), address);
     }
 }

Or alternatively if you want to use a LIKE query, you could use:或者,如果您想使用LIKE查询,您可以使用:

public class SomeSpecs {
     public static Specification<Some> withAddress(String address) {
          return (root, query, builder) -> builder.like(root.get("address"), "%" + address + "%");
     }
 }

Now you have to repeat this for the other fields you want to filter on.现在您必须对要过滤的其他字段重复此操作。 After that you'll have to use all specifications together (using and() , or() , ...).之后,您必须一起使用所有规范(使用and()or() 、...)。 Then you can use the repository.findAll(Specification) method to query based on that specification, for example:然后你可以使用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));
}

You can use static imports to import withAddress() , withName() and withDate() to make it easier to read.您可以使用静态导入来导入withAddress()withName()withDate()以使其更易于阅读。 The where() method can also be statically imported (comes from Specification.where() ). where()方法也可以静态导入(来自Specification.where() )。

Be aware though that the method above may have to be tweaked since you don't want to filter on the address field if it's null .请注意,上面的方法可能需要进行调整,因为如果地址字段为null则您不想过滤它。 You could do this by returning null , for example:您可以通过返回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));
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM