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
}

前面的示例显示了一个简单的域对象。你可以使用它来创建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

你可以使用存储库来运行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.
}

三、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

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

ExampleMatcher matcher = ExampleMatcher.matching()
  .withMatcher("firstname", endsWith())
  .withMatcher("lastname", startsWith().ignoreCase());
}

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

ExampleMatcher matcher = ExampleMatcher.matching()
  .withMatcher("firstname", match -> match.endsWith())
  .withMatcher("firstname", match -> match.startsWith());
}

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提供了另一种方法,是我们到目前为止还没有提到的:“<S extends T, R> R findBy(Example< S> example, Function<FluentQuery.FetchableFluentQuery< S>, 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()
);

五、运行一个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));
  }
}
  • 29
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值