1. Introduction
1.绪论
In this tutorial, we’re going to learn how to query data with the Spring Data Query by Example API.
在本教程中,我们将学习如何使用Spring Data Query by Example API来查询数据。
First, we’ll define the schema of the data we want to query. Next, we’ll examine a few of the relevant classes from Spring Data. And then, we’ll run through a few examples.
首先,我们将定义我们要查询的数据的模式。接下来,我们将研究Spring Data中的一些相关类。然后,我们将运行几个例子。
Let’s get started!
让我们开始吧!
2. The Test Data
2.测试数据
Our test data is a list of passenger names as well as the seat they occupied.
我们的测试数据是一份乘客姓名以及他们所占座位的清单。
First Name | Last Name | Seat Number |
---|---|---|
Jill | Smith | 50 |
Eve | Jackson | 94 |
Fred | Bloggs | 22 |
Ricki | Bobbie | 36 |
Siya | Kolisi | 85 |
3. Domain
3.领域
Let’s create the Spring Data Repository we need and provide our domain class and id type.
让我们创建我们需要的Spring数据存储库,并提供我们的域类和id类型。
To begin with, we’ve modeled our Passenger as a JPA entity:
首先,我们将我们的Passenger建模为一个JPA实体。
@Entity
class Passenger {
@Id
@GeneratedValue
@Column(nullable = false)
private Long id;
@Basic(optional = false)
@Column(nullable = false)
private String firstName;
@Basic(optional = false)
@Column(nullable = false)
private String lastName;
@Basic(optional = false)
@Column(nullable = false)
private int seatNumber;
// constructor, getters etc.
}
Instead of using JPA, we could’ve modeled it as another abstraction.
我们可以不使用JPA,而是将其作为另一个抽象的模型。
4. Query by Example API
4.按实例查询API
Firstly, let’s take a look at the JpaRepository interface. As we can see it extends the QueryByExampleExecutor interface to support query by example:
首先,让我们看一下JpaRepository接口。我们可以看到它扩展了QueryByExampleExecutor接口,以支持通过实例查询。
public interface JpaRepository<T, ID>
extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> {}
This interface introduces more variants of the find() method that we’re familiar with from Spring Data. However, each method also accepts an instance of Example:
这个接口引入了我们在Spring Data中熟悉的find()方法的更多变体。然而,每个方法也接受Example的一个实例。
public interface QueryByExampleExecutor<T> {
<S extends T> Optional<S> findOne(Example<S> var1);
<S extends T> Iterable<S> findAll(Example<S> var1);
<S extends T> Iterable<S> findAll(Example<S> var1, Sort var2);
<S extends T> Page<S> findAll(Example<S> var1, Pageable var2);
<S extends T> long count(Example<S> var1);
<S extends T> boolean exists(Example<S> var1);
}
Secondly, the Example interface exposes methods to access the probe and the ExampleMatcher.
其次,Example接口暴露了访问probe和ExampleMatcher的方法。
It’s important to realize that the probe is the instance of our Entity:
重要的是要认识到,probe是我们Entity的实例。
public interface Example<T> {
static <T> org.springframework.data.domain.Example<T> of(T probe) {
return new TypedExample(probe, ExampleMatcher.matching());
}
static <T> org.springframework.data.domain.Example<T> of(T probe, ExampleMatcher matcher) {
return new TypedExample(probe, matcher);
}
T getProbe();
ExampleMatcher getMatcher();
default Class<T> getProbeType() {
return ProxyUtils.getUserClass(this.getProbe().getClass());
}
}
In summary, our probe and our ExampleMatcher together specify our query.
总之,我们的probe和ExampleMatcher一起指定了我们的查询。
5. Limitations
5.限制条件
Like all things, the Query by Example API has some limitations. For instance:
像所有的东西一样,”实例查询 “API有一些限制。比如说。
- Nesting and grouping statements are not supported, for example: (firstName = ?0 and lastName = ?1) or seatNumber = ?2
- String matching only includes exact, case-insensitive, starts, ends, contains, and regex
- All types other than String are exact-match only
Now that we’re a little more familiar with the API and its limitations, let’s dive into some examples.
现在我们对API和它的限制有了更多的了解,让我们深入了解一些例子。
6. Examples
6.实例
6.1. Case-Sensitive Matching
6.1.区分大小写的匹配
Let’s start with a simple example and talk about the default behavior:
让我们从一个简单的例子开始,谈谈默认行为。
@Test
public void givenPassengers_whenFindByExample_thenExpectedReturned() {
Example<Passenger> example = Example.of(Passenger.from("Fred", "Bloggs", null));
Optional<Passenger> actual = repository.findOne(example);
assertTrue(actual.isPresent());
assertEquals(Passenger.from("Fred", "Bloggs", 22), actual.get());
}
In particular, the static Example.of() method builds an Example using ExampleMatcher.matching().
特别是,静态的Example.of()方法使用ExampleMatcher.matching()构建了一个Example。
In other words, an exact match will be performed on all non-null properties of Passenger. Thus, the matching is case-sensitive on String properties.
换句话说,精确匹配将在Passenger的所有非空的属性上进行。因此,对String属性的匹配是区分大小写的。
However, it wouldn’t be too useful if all we could do was an exact match on all non-null properties.
然而,如果我们能做的只是对所有非空属性进行精确匹配,那就不是太有用了。
This is where the ExampleMatcher comes in. By building our own ExampleMatcher, we can customize the behavior to suit our needs.
这就是ExampleMatcher的用处。通过构建我们自己的ExampleMatcher,我们可以定制行为以满足我们的需求。
6.2. Case-Insensitive Matching
6.2.不区分大小写的匹配
With that in mind, let’s have a look at another example, this time using withIgnoreCase() to achieve case-insensitive matching:
考虑到这一点,让我们看看另一个例子,这次是使用withIgnoreCase()来实现不区分大小写的匹配。
@Test
public void givenPassengers_whenFindByExampleCaseInsensitiveMatcher_thenExpectedReturned() {
ExampleMatcher caseInsensitiveExampleMatcher = ExampleMatcher.matchingAll().withIgnoreCase();
Example<Passenger> example = Example.of(Passenger.from("fred", "bloggs", null),
caseInsensitiveExampleMatcher);
Optional<Passenger> actual = repository.findOne(example);
assertTrue(actual.isPresent());
assertEquals(Passenger.from("Fred", "Bloggs", 22), actual.get());
}
In this example, notice that we first called ExampleMatcher.matchingAll() – it has the same behavior as ExampleMatcher.matching(), which we used in the previous example.
在这个例子中,注意到我们首先调用了ExampleMatcher.matchingAll() –它的行为与我们在前一个例子中使用的ExampleMatcher.matching()相同。
6.3. Custom Matching
6.3.自定义匹配
We can also tune the behavior of our matcher on a per-property basis and match any property using ExampleMatcher.matchingAny():
我们还可以在每个属性的基础上调整我们的匹配器的行为,并使用ExampleMatcher.matchAny()匹配任何属性。
@Test
public void givenPassengers_whenFindByExampleCustomMatcher_thenExpectedReturned() {
Passenger jill = Passenger.from("Jill", "Smith", 50);
Passenger eve = Passenger.from("Eve", "Jackson", 95);
Passenger fred = Passenger.from("Fred", "Bloggs", 22);
Passenger siya = Passenger.from("Siya", "Kolisi", 85);
Passenger ricki = Passenger.from("Ricki", "Bobbie", 36);
ExampleMatcher customExampleMatcher = ExampleMatcher.matchingAny()
.withMatcher("firstName", ExampleMatcher.GenericPropertyMatchers.contains().ignoreCase())
.withMatcher("lastName", ExampleMatcher.GenericPropertyMatchers.contains().ignoreCase());
Example<Passenger> example = Example.of(Passenger.from("e", "s", null), customExampleMatcher);
List<Passenger> passengers = repository.findAll(example);
assertThat(passengers, contains(jill, eve, fred, siya));
assertThat(passengers, not(contains(ricki)));
}
6.4. Ignoring Properties
6.4.忽略属性
On the other hand, we may also only want to query on a subset of our properties.
另一方面,我们也可能只想对我们的属性的子集进行查询。
We achieve this by ignoring some properties using ExampleMatcher.ignorePaths(String… paths):
我们通过使用ExampleMatcher.ignorePaths(String…paths)忽略一些属性来实现这一点。
@Test
public void givenPassengers_whenFindByIgnoringMatcher_thenExpectedReturned() {
Passenger jill = Passenger.from("Jill", "Smith", 50);
Passenger eve = Passenger.from("Eve", "Jackson", 95);
Passenger fred = Passenger.from("Fred", "Bloggs", 22);
Passenger siya = Passenger.from("Siya", "Kolisi", 85);
Passenger ricki = Passenger.from("Ricki", "Bobbie", 36);
ExampleMatcher ignoringExampleMatcher = ExampleMatcher.matchingAny()
.withMatcher("lastName", ExampleMatcher.GenericPropertyMatchers.startsWith().ignoreCase())
.withIgnorePaths("firstName", "seatNumber");
Example<Passenger> example = Example.of(Passenger.from(null, "b", null), ignoringExampleMatcher);
List<Passenger> passengers = repository.findAll(example);
assertThat(passengers, contains(fred, ricki));
assertThat(passengers, not(contains(jill));
assertThat(passengers, not(contains(eve));
assertThat(passengers, not(contains(siya));
}
7. Conclusion
7.结语
In this article, we’ve demonstrated how to use the Query by Example API.
在这篇文章中,我们演示了如何使用 “实例查询 “的API。
We’ve demonstrated how to use Example and ExampleMatcher along with the QueryByExampleExecutor interface to query a table using an example data instance.
我们已经演示了如何使用Example和ExampleMatcher以及QueryByExampleExecutor接口来使用示例数据实例查询一个表。
In conclusion, you can find the code over on GitHub.
总之,你可以在GitHub上找到代码over。