• Spring Data的Repositories----Query by Example


    【Spring连载】使用Spring Data的Repositories----Query by Example

    一、概述

    本章介绍了Query by Example,并解释了如何使用它。
    Query by Example(QBE)是一种接口简单、用户友好的查询技术。它允许动态创建查询,不需要编写包含字段名的查询。事实上,QBE根本不需要您使用特定于存储的查询语言来编写查询。

    二、用法

    QBE API由四部分组成:

    • Probe:具有填充字段的域对象的实际example。
    • ExampleMatcher:ExampleMatcher提供有关如何匹配特定字段的详细信息。它可以在多个Examples中重复使用。
    • Example:Example由probe和ExampleMatcher组成。它用于创建查询。
    • FetchableFluentQuery:FetchableFluentQuery提供了一个fluent API,它允许进一步自定义从Example派生的查询。使用fluent API可以指定查询的排序投影(projection )和结果处理。

    “Query by Example”非常适合几种用例:

    • 使用一组静态或动态约束查询数据存储。
    • 频繁重构域对象,而不用担心破坏现有查询。
    • 独立于底层数据存储API工作。

    “Query by Example”也有几个限制:

    • 不支持嵌套或分组的属性约束,例如firstname = ?0 or (firstname = ?1 and lastname = ?2)。
    • 仅支持字符串的starts/contains/ends/regex匹配和其他属性类型的精确匹配。

    在开始使用Query by Example之前,你需要有一个域对象。要开始,请为存储库创建一个接口,如以下示例所示:
    示例Person对象

    public class Person {
    
      @Id
      private String id;
      private String firstname;
      private String lastname;
      private Address address;
    
      // … getters and setters omitted
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    前面的示例显示了一个简单的域对象。你可以使用它来创建Example。默认情况下,忽略具有null值的字段,并使用特定于存储的默认值匹配字符串。
    将属性包含到“Query by Example”条件中是基于可空性(nullability)的。除非ExampleMatcher(章节11.3)忽略属性路径,否则始终包括使用原始类型(int, double, …) 的属性。
    Examples可以通过使用of工厂方法或使用ExampleMatcher来构建。Example是不可变的。下面的列表展示了一个简单的示例:
    例1:简单示例

    Person person = new Person();             --------1            
    person.setFirstname("Dave");              --------2            
    
    Example<Person> example = Example.of(person);  --------3       
    
    1. 创建域对象的新实例。
    2. 设置要查询的属性。
    3. 创建Example
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    你可以使用存储库来运行example查询。为此,请让你的存储库接口继承QueryByExampleExecutor。以下列表展示了QueryByExampleExecutor接口的片段:
    QueryByExampleExecutor

    public interface QueryByExampleExecutor<T> {
    
      <S extends T> S findOne(Example<S> example);
    
      <S extends T> Iterable<S> findAll(Example<S> example);
    
      // … more functionality omitted.
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    三、Example Matchers

    Examples不限于默认设置。你可以使用ExampleMatcher为字符串匹配、null处理和属性特定设置指定自己的默认值,如下例所示:
    例2:具有自定义匹配的Example matcher

    Person person = new Person();                          --------1
    person.setFirstname("Dave");                           --------2
                                                           
    ExampleMatcher matcher = ExampleMatcher.matching()     --------3
      .withIgnorePaths("lastname")                         --------4
      .withIncludeNullValues()                             --------5
      .withStringMatcher(StringMatcher.ENDING);            --------6
    
    Example<Person> example = Example.of(person, matcher); --------7
    
    1. 创建域对象的新实例。
    2. 设置属性。
    3. 创建ExampleMatcher以期望所有值都匹配。即使没有进一步的配置,它也可以在这个阶段使用。
    4. 构造一个新的ExampleMatcher,忽略lastname属性路径。
    5. 构造一个新的ExampleMatcher,忽略lastname属性路径并包含null值。
    6. 构造一个新的ExampleMatcher,忽略lastname属性路径,包括null值,并执行后缀字符串匹配。
    7. 基于域对象和配置的ExampleMatcher创建一个新的Example
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    默认情况下,ExampleMatcher希望probe上设置的所有值都匹配。如果要获得与隐式定义的任何predicates匹配的结果,请使用ExampleMatcher.matchingAny()。
    你可以指定单个属性的行为(例如“firstname”和“lastname”,或者对于嵌套属性,如“address.city”)。你可以使用匹配选项和区分大小写对其进行调整,如以下示例所示:
    配置匹配器选项

    ExampleMatcher matcher = ExampleMatcher.matching()
      .withMatcher("firstname", endsWith())
      .withMatcher("lastname", startsWith().ignoreCase());
    }
    
    • 1
    • 2
    • 3
    • 4

    配置匹配器选项的另一种方法是使用lambdas(在Java8中引入)。这种方法创建一个回调,要求实现者修改匹配器。你不需要返回matcher,因为配置选项保存在matcher实例中。以下示例显示了一个使用lambdas的匹配器:
    使用lambdas配置匹配器选项

    ExampleMatcher matcher = ExampleMatcher.matching()
      .withMatcher("firstname", match -> match.endsWith())
      .withMatcher("firstname", match -> match.startsWith());
    }
    
    • 1
    • 2
    • 3
    • 4

    Example创建的查询使用配置的合并视图(view)。默认匹配设置可以在ExampleMatcher级别设置,而单独的设置可以应用于特定的属性路径。在ExampleMatcher上的设置由属性路径设置继承,除非它们是明确定义的。属性patch上的设置的优先级高于默认设置。下表介绍了各种ExampleMatcher设置的范围:
    表1:ExampleMatcher设置的范围

    SettingScope
    Null-handlingExampleMatcher
    String matchingExampleMatcher and property path
    Ignoring propertiesProperty path
    Case sensitivityExampleMatcher and property path
    Value transformationProperty path

    四、Fluent API

    QueryByExampleExecutor提供了另一种方法,是我们到目前为止还没有提到的:“ R findBy(Example< S> example, Function, R> queryFunction)“。与其他方法一样,它执行从Example派生的查询。但是,使用第二个参数,你可以控制该执行的某些方面,而这些方面在其他方面是无法动态控制的。你可以通过在第二个自变量中调用FetchableFluentQuery的各种方法来实现这一点。sortBy允许你为结果指定排序。as 允许你指定所需结果的类型以进行转换。project限制了查询的属性。first, firstValue, one, oneValue, all, page, stream, count和exists定义了当可用的结果数量超过预期数量时,得到的结果类型以及查询的行为。
    使用fluent API获取降序结果中的第一个,按lastname排序

    Optional<Person> match = repository.findBy(example,
        q -> q
            .sortBy(Sort.by("lastname").descending())
            .first()
    );
    
    • 1
    • 2
    • 3
    • 4
    • 5

    五、运行一个Example

    以下示例对存储库使用“Query by Example”:
    例3:使用存储库按示例查询

    interface PersonRepository extends QueryByExampleExecutor<Person> {
    }
    
    class PersonService {
    
      @Autowired PersonRepository personRepository;
    
      List<Person> findPeople(Person probe) {
        return personRepository.findAll(Example.of(probe));
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
  • 相关阅读:
    视频监控系统/视频汇聚平台EasyCVR如何反向代理进行后端保活?
    在 Net7.0环境下通过反射创建对象和调用方法
    SwiftUI 布局大全之探索 SwiftUI 布局协议——创建自定义布局
    03-系统篇-内存碎片
    18、Java的类变量、类方法;static 关键字;静态导入;初始化块;静态初始化块;单例模式
    Shell 脚本循环遍历日志文件中的值进行求和并计算平均值,最大值和最小值
    IDOC-外围系统发送客户银行数据,SAP生成入站IDOC
    Loongnix单机部署Ceph(LoongArch架构、Ceph N版、手动部署MON、OSD、MGR、Dashboard服务)
    制作翻页电子画册,手机观看更快捷
    halcon 感兴趣区域
  • 原文地址:https://blog.csdn.net/gabriel_wang_sh/article/details/136566744