Query by Example
Introduction
本章介绍了按示例查询并说明如何使用它。
This chapter provides an introduction to Query by Example and explains how to use it.
按示例查询 (QBE) 是一种具有简单界面的用户友好查询技术。它允许动态创建查询,并且不要求您编写包含字段名称的查询。事实上,按示例查询根本不要求您使用特定于存储的查询语言编写查询。
Query by Example (QBE) is a user-friendly querying technique with a simple interface. It allows dynamic query creation and does not require you to write queries that contain field names. In fact, Query by Example does not require you to write queries by using store-specific query languages at all.
本小节介绍 Query by Example 的核心概念。该信息从 Spring Data Commons 模块中提取。根据您的数据库,字符串匹配支持可能受限。 |
This chapter explains the core concepts of Query by Example. The information is pulled from the Spring Data Commons module. Depending on your database, String matching support can be limited. |
Usage
按示例查询 API 包含四个部分:
The Query by Example API consists of four parts:
-
Probe: The actual example of a domain object with populated fields.
-
ExampleMatcher
: TheExampleMatcher
carries details on how to match particular fields. It can be reused across multiple Examples. -
Example
: AnExample
consists of the probe and theExampleMatcher
. It is used to create the query. -
FetchableFluentQuery
: AFetchableFluentQuery
offers a fluent API, that allows further customization of a query derived from anExample
. Using the fluent API lets you specify ordering projection and result processing for your query.
按示例查询非常适合几种用例:
Query by Example is well suited for several use cases:
-
Querying your data store with a set of static or dynamic constraints.
-
Frequent refactoring of the domain objects without worrying about breaking existing queries.
-
Working independently of the underlying data store API.
按示例查询也有一些限制:
Query by Example also has several limitations:
-
No support for nested or grouped property constraints, such as
firstname = ?0 or (firstname = ?1 and lastname = ?2)
. -
Store-specific support on string matching. Depending on your databases, String matching can support starts/contains/ends/regex for strings.
-
Exact matching for other property types.
在开始使用按示例查询之前,您需要有一个域对象。要开始,请为您的存储库创建一个接口,如下例所示:
Before getting started with Query by Example, you need to have a domain object. To get started, create an interface for your repository, as shown in the following example:
public class Person {
@Id
private String id;
private String firstname;
private String lastname;
private Address address;
// … getters and setters omitted
}
前面的示例显示了一个简单的域对象。您可以用它来创建示例。默认情况下,值为 null 的字段将被忽略,而字符串将使用特定于存储的默认值进行匹配。
The preceding example shows a simple domain object.
You can use it to create an Example
.
By default, fields having null
values are ignored, and strings are matched by using the store specific defaults.
将属性包含到 Query by Example 标准中基于空值能力。使用基本类型( |
Inclusion of properties into a Query by Example criteria is based on nullability.
Properties using primitive types ( |
可以通过使用 of 工厂方法或使用 [ExampleMatcher,查询 by示例。匹配器 ]. Example 是不可变的。以下清单显示了一个简单的示例:
Examples can be built by either using the of
factory method or by using <<`ExampleMatcher`,query-by-example.matchers>>. Example
is immutable.
The following listing shows a simple Example:
Person person = new Person(); 1
person.setFirstname("Dave"); 2
Example<Person> example = Example.of(person); 3
1 | Create a new instance of the domain object. |
2 | Set the properties to query. |
3 | Create the Example . |
您可以使用存储库运行示例查询。要做到这一点,让您的存储库接口扩展 QueryByExampleExecutor<T>。以下清单显示了 QueryByExampleExecutor 接口的摘录:
You can run the example queries by using repositories.
To do so, let your repository interface extend QueryByExampleExecutor<T>
.
The following listing shows an excerpt from the QueryByExampleExecutor
interface:
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
示例不仅限于默认设置。您可以使用 ExampleMatcher 指定字符串匹配、null 处理和特定于属性的设置的默认值,如下例所示:
Examples are not limited to default settings.
You can specify your own defaults for string matching, null handling, and property-specific settings by using the ExampleMatcher
, as shown in the following example:
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 | Create a new instance of the domain object. |
2 | Set properties. |
3 | Create an ExampleMatcher to expect all values to match.
It is usable at this stage even without further configuration. |
4 | Construct a new ExampleMatcher to ignore the lastname property path. |
5 | Construct a new ExampleMatcher to ignore the lastname property path and to include null values. |
6 | Construct a new ExampleMatcher to ignore the lastname property path, to include null values, and to perform suffix string matching. |
7 | Create a new Example based on the domain object and the configured ExampleMatcher . |
默认情况下,ExampleMatcher 期望探测器上设置的所有值都匹配。如果您想要获得与隐式定义的任何谓词匹配的结果,请使用 ExampleMatcher.matchingAny()。
By default, the ExampleMatcher
expects all values set on the probe to match.
If you want to get results matching any of the predicates defined implicitly, use ExampleMatcher.matchingAny()
.
您可以指定单个属性(例如“firstname”和“lastname”,或对于嵌套属性,“address.city”)的行为。您可以用匹配选项和大写/小写敏感性对其进行微调,如下例所示:
You can specify behavior for individual properties (such as "firstname" and "lastname" or, for nested properties, "address.city"). You can tune it with matching options and case sensitivity, as shown in the following example:
ExampleMatcher matcher = ExampleMatcher.matching()
.withMatcher("firstname", endsWith())
.withMatcher("lastname", startsWith().ignoreCase());
}
配置匹配器选项的另一种方法是使用 lambda(在 Java 8 中引入)。此方法创建一个回调,要求实现者修改匹配器。您无需返回匹配器,因为配置选项保存在匹配器实例中。以下示例显示了一个使用 lambda 的匹配器:
Another way to configure matcher options is to use lambdas (introduced in Java 8). This approach creates a callback that asks the implementor to modify the matcher. You need not return the matcher, because configuration options are held within the matcher instance. The following example shows a matcher that uses lambdas:
ExampleMatcher matcher = ExampleMatcher.matching()
.withMatcher("firstname", match -> match.endsWith())
.withMatcher("firstname", match -> match.startsWith());
}
由 Example 创建的查询使用配置的合并视图。默认匹配设置可以设置在 ExampleMatcher 级别,而各个设置可以应用于特定的属性路径。除非显式定义,否则在 ExampleMatcher 上设置的设置将由属性路径设置继承。属性补丁上的设置优先于默认设置。下表描述了各种 ExampleMatcher 设置的范围:
Queries created by Example
use a merged view of the configuration.
Default matching settings can be set at the ExampleMatcher
level, while individual settings can be applied to particular property paths.
Settings that are set on ExampleMatcher
are inherited by property path settings unless they are defined explicitly.
Settings on a property patch have higher precedence than default settings.
The following table describes the scope of the various ExampleMatcher
settings:
Setting | Scope |
---|---|
Null-handling |
|
String matching |
|
Ignoring properties |
Property path |
Case sensitivity |
|
Value transformation |
Property 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 定义了您获得什么样的结果,以及在可用结果多于预期数量时查询将如何进行。
QueryByExampleExecutor
offers one more method, which we did not mention so far: <S extends T, R> R findBy(Example<S> example, Function<FluentQuery.FetchableFluentQuery<S>, R> queryFunction)
.
As with other methods, it executes a query derived from an Example
.
However, with the second argument, you can control aspects of that execution that you cannot dynamically control otherwise.
You do so by invoking the various methods of the FetchableFluentQuery
in the second argument.
sortBy
lets you specify an ordering for your result.
as
lets you specify the type to which you want the result to be transformed.
project
limits the queried attributes.
first
, firstValue
, one
, oneValue
, all
, page
, stream
, count
, and exists
define what kind of result you get and how the query behaves when more than the expected number of results are available.
Optional<Person> match = repository.findBy(example,
q -> q
.sortBy(Sort.by("lastname").descending())
.first()
);
这是一个示例:
Here’s an example:
Unresolved include directive in modules/ROOT/pages/query-by-example.adoc - include::example$r2dbc/QueryByExampleTests.java[]
1 | Create a domain object with the criteria (null fields will be ignored). |
2 | Using the domain object, create an Example . |
3 | Through the repository, execute query (use findOne for a single item). |
这说明了如何使用领域对象制作一个简单的探测。在这种情况下,它将基于 Employee`对象的`name`字段等于 `Frodo
来进行查询。null
字段被忽略。
This illustrates how to craft a simple probe using a domain object.
In this case, it will query based on the Employee
object’s name
field being equal to Frodo
.
null
fields are ignored.
Unresolved include directive in modules/ROOT/pages/query-by-example.adoc - include::example$r2dbc/QueryByExampleTests.java[]
1 | Create a custom ExampleMatcher that matches on ALL fields (use matchingAny() to match on ANY fields) |
2 | For the name field, use a wildcard that matches against the end of the field |
3 | Match columns against null (don’t forget that NULL doesn’t equal NULL in relational databases). |
4 | Ignore the role field when forming the query. |
5 | Plug the custom ExampleMatcher into the probe. |
还可以对任何属性应用 withTransform()
,允许你在形成查询之前转换一个属性。例如,可以在创建查询之前对基于 String
的属性应用 toUpperCase()
。
It’s also possible to apply a withTransform()
against any property, allowing you to transform a property before forming the query.
For example, you can apply a toUpperCase()
to a String
-based property before the query is created.
当你事先不知道查询中所需的所有字段时,按示例查询非常有用。如果要在用户可以选择字段的网页上构建一个筛选器,那么按示例查询是一种灵活地将它捕获到一个高效查询中的不错方式。
Query By Example really shines when you don’t know all the fields needed in a query in advance. If you were building a filter on a web page where the user can pick the fields, Query By Example is a great way to flexibly capture that into an efficient query.