Spring认证中国教育管理中心-Spring Data Neo4j教程五
发布于 2 年前 作者 hchen 521 次浏览 来自 分享

原标题:Spring认证中国教育管理中心-Spring Data Neo4j教程五(Spring中国教育管理中心)

Spring认证:Spring Data Neo4j教程五
7.4.定义查询方法
存储库代理有两种方法可以从方法名称派生特定于存储的查询:

通过直接从方法名称派生查询。
通过使用手动定义的查询。
可用选项取决于实际商店。但是,必须有一种策略来决定创建什么实际查询。下一节将介绍可用的选项。

7.4.1。查询查找策略
以下策略可用于存储库基础架构来解决查询。使用 XML 配置,您可以通过query-lookup-strategy属性在命名空间配置策略。对于 Java 配置,您可以使用注解的queryLookupStrategy属性。Enable${store}Repositories特定数据存储可能不支持某些策略。

CREATE尝试从查询方法名称构造特定于存储的查询。一般的方法是从方法名称中删除一组给定的已知前缀并解析方法的其余部分。您可以在“第 7.4.2 节”中阅读有关查询构造的更多信息。
USE_DECLARED_QUERY尝试查找已声明的查询,如果找不到则抛出异常。查询可以由某处的注释定义或通过其他方式声明。请参阅特定商店的文档以查找该商店​的可用选项。如果存储库基础结构在引导时没有找到该方法的声明查询,它就会失败。
CREATE_IF_NOT_FOUND(默认)结合CREATE和USE_DECLARED_QUERY。它首先查找已声明的查询,如果未找到已声明的查询,则创建一个基于自定义方法名称的查询。这是默认查找策略,因此,如果您未明确配置任何内容,则使用此策略。它允许通过方法名称快速定义查询,还可以通过根据需要引入声明的查询来自定义调整这些查询。
7.4.2.查询创建
Spring Data 存储库基础结构中内置的查询构建器机制对于在存储库的实体上构建约束查询很有用。

以下示例显示了如何创建多个查询:

示例 23. 从方法名称创建查询

interface PersonRepository extends Repository<Person, Long> {

List<Person> findByEmailAddressAndLastname(EmailAddress emailAddress, String lastname);

// Enables the distinct flag for the query
List<Person> findDistinctPeopleByLastnameOrFirstname(String lastname, String firstname);
List<Person> findPeopleDistinctByLastnameOrFirstname(String lastname, String firstname);

// Enabling ignoring case for an individual property
List<Person> findByLastnameIgnoreCase(String lastname);
// Enabling ignoring case for all suitable properties
List<Person> findByLastnameAndFirstnameAllIgnoreCase(String lastname, String firstname);

// Enabling static ORDER BY for a query
List<Person> findByLastnameOrderByFirstnameAsc(String lastname);
List<Person> findByLastnameOrderByFirstnameDesc(String lastname);
}
解析查询方法名称分为主语和谓语。第一部分 ( find…By, exists…By) 定义查询的主题,第二部分构成谓词。引言从句(主语)可以包含进一步的表达。find(或其他介绍关键字)和之间的任何文本By都被认为是描述性的,除非使用结果限制关键字之一,例如在Distinct要创建的查询上设置不同的标志或Top/First来限制查询结果。

附录包含查询方法主题关键字和查询方法谓词关键字的完整列表,包括排序和字母大小写修饰符。但是,第一个By用作分隔符以指示实际条件谓词的开始。在非常基本的级别上,您可以在实体属性上定义条件并将它们与And和连接起来Or。

解析方法的实际结果取决于您为其创建查询的持久性存储。但是,有一些一般的事情需要注意:

表达式通常是结合了可以连接的运算符的属性遍历。您可以将属性表达式与AND和结合使用OR。您还可以获得对运算符(如Between、LessThan、和 )的支持GreaterThan,以及Like对属性表达式的支持。支持的运算符可能因数据存储而异,因此请参阅参考文档的相应部分。
方法解析器支持IgnoreCase为单个属性(例如,findByLastnameIgnoreCase(…))或支持忽略大小写的类型的所有属性(通常是String实例 - 例如,findByLastnameAndFirstnameAllIgnoreCase(…))设置标志。是否支持忽略大小写可能因商店而异,因此请参阅参考文档中的相关部分以了解商店特定的查询方法。
您可以通过将子句附加OrderBy到引用属性的查询方法并提供排序方向(Asc或Desc)来应用静态排序。要创建支持动态排序的查询方法,请参阅“第 7.4.4 节”。
7.4.3.属性表达式
属性表达式只能引用托管实体的直接属性,如前面的示例所示。在创建查询时,您已经确保解析的属性是托管域类的属性。但是,您也可以通过遍历嵌套属性来定义约束。考虑以下方法签名:

List<Person> findByAddressZipCode(ZipCode zipCode);
假设 aPerson有Address一个ZipCode。在这种情况下,该方法会创建x.address.zipCode属性遍历。解析算法首先将整个部分 ( AddressZipCode) 解释为属性,并检查域类中具有该名称(未大写)的属性。如果算法成功,它将使用该属性。如果不是,该算法将源在驼峰部分从右侧拆分为头部和尾部,并尝试找到相应的属性——在我们的示例中,AddressZip和Code。如果算法找到具有该头部的属性,它将获取尾部并继续从那里向下构建树,以刚才描述的方式将尾部拆分。如果第一个分割不匹配,算法将分割点向左移动 ( Address,ZipCode) 并继续。

虽然这应该适用于大多数情况,但算法可能会选择错误的属性。假设这个Person类也有一个addressZip属性。该算法已经在第一个拆分轮中匹配,选择了错误的属性,然后失败(因为 的类型addressZip可能没有code属性)。

要解决这种歧义,您可以_在方法名称中使用手动定义遍历点。所以我们的方法名称如下:

List<Person> findByAddress_ZipCode(ZipCode zipCode);
因为我们将下划线字符视为保留字符,我们强烈建议遵循标准的 Java 命名约定(即,不在属性名称中使用下划线,而是使用驼峰式大小写)。

7.4.4.特殊参数处理
要处理查询中的参数,请定义前面示例中已经看到的方法参数。除此之外,该基础架构还可以识别某些特定类型,例如Pageableand Sort,以便动态地将分页和排序应用于您的查询。以下示例演示了这些功能:

示例 24.在查询方法中使用Pageable、、Slice和Sort

Page<User> findByLastname(String lastname, Pageable pageable);

Slice<User> findByLastname(String lastname, Pageable pageable);

List<User> findByLastname(String lastname, Sort sort);

List<User> findByLastname(String lastname, Pageable pageable);
API 接受Sort并Pageable期望将非null值传递给方法。如果您不想应用任何排序或分页,请使用Sort.unsorted()and Pageable.unpaged()。

第一种方法允许您将
org.springframework.data.domain.Pageable实例传递给查询方法,以动态地将分页添加到静态定义的查询中。APage知道可用元素和页面的总数。它通过基础设施触发计数查询来计算总数来实现这一点。由于这可能很昂贵(取决于使用的商店),您可以改为返回Slice. ASlice只知道下一个Slice是否可用,这在遍历更大的结果集时可能就足够了。

排序选项也通过Pageable实例处理。如果您只需要排序,
org.springframework.data.domain.Sort请在您的方法中添加一个参数。如您所见,返回 aList也是可能的。在这种情况下,不会创建构建实际实例所需的额外元数据Page(这反过来意味着不会发出本来需要的额外计数查询)。相反,它将查询限制为仅查找给定范围的实体。

要了解整个查询获得了多少页,您必须触发额外的计数查询。默认情况下,此查询派生自您实际触发的查询。

分页和排序
您可以使用属性名称定义简单的排序表达式。您可以连接表达式以将多个条件收集到一个表达式中。

示例 25.定义排序表达式

Sort sort = Sort.by(“firstname”).ascending()
.and(Sort.by(“lastname”).descending());
要使用更安全的方式来定义排序表达式,请从要为其定义排序表达式的类型开始,并使用方法引用来定义要排序的属性。

示例 26. 使用类型安全 API 定义排序表达式

TypedSort<Person> person = Sort.sort(Person.class);

Sort sort = person.by(Person::getFirstname).ascending()
.and(person.by(Person::getLastname).descending());
TypedSort.by(…) 通过(通常)使用 CGlib 来使用运行时代理,这可能会在使用 Graal VM Native 等工具时干扰本机映像编译。

如果您的商店实现支持 Querydsl,您还可以使用生成的元模型类型来定义排序表达式:

示例 27. 使用 Querydsl API 定义排序表达式

QSort sort = QSort.by(QPerson.firstname.asc())
.and(QSort.by(QPerson.lastname.desc()));
7.4.5限制查询结果
first您可以使用or关键字来限制查询方法的结果top,您可以互换使用它们。您可以将可选数值附加到top或first指定要返回的最大结果大小。如果省略该数字,则假定结果大小为 1。以下示例显示了如何限制查询大小:

Top示例 28. 使用and限制查询的结果大小First

User findFirstByOrderByLastnameAsc();

User findTopByOrderByAgeDesc();

Page<User> queryFirst10ByLastname(String lastname, Pageable pageable);

Slice<User> findTop3ByLastname(String lastname, Pageable pageable);

List<User> findFirst10ByLastname(String lastname, Sort sort);

List<User> findTop10ByLastname(String lastname, Pageable pageable);
限制表达式还支持Distinct支持不同查询的数据存储的关键字。Optional此外,对于将结果集限制为一个实例的查询,支持使用关键字将结果包装到其中。

如果分页或切片应用于限制查询分页(以及可用页数的计算),则在限制结果中应用。

通过使用参数限制结果与动态排序相结合,Sort您可以表达“K”最小元素和“K”最大元素的查询方法。

7.4.6返回集合或迭代的存储库方法
返回多个结果的查询方法可以使用标准的 Java Iterable、List和Set. 除此之外,我们还支持返回 Spring Data 的Streamable自定义扩展Iterable,以及Vavr提供的集合类型。请参阅解释所有可能的查询方法返回类型的附录。

使用 Streamable 作为查询方法返回类型
您可以Streamable用作任何集合类型的替代品Iterable或任何集合类型。它提供了方便的方法来访问非并行Stream(缺少Iterable)以及直接….filter(…)和….map(…)覆盖元素并将其连接Streamable到其他元素的能力:

示例 29. 使用 Streamable 组合查询方法结果

interface PersonRepository extends Repository<Person, Long> {
Streamable<Person> findByFirstnameContaining(String firstname);
Streamable<Person> findByLastnameContaining(String lastname);
}

Streamable<Person> result = repository.findByFirstnameContaining(“av”)
.and(repository.findByLastnameContaining(“ea”));
返回自定义 Streamable Wrapper 类型
为集合提供专用的包装器类型是为返回多个元素的查询结果提供 API 的常用模式。通常,通过调用返回类似集合类型的存储库方法并手动创建包装器类型的实例来使用这些类型。您可以避免该额外步骤,因为 Spring Data 允许您将这些包装器类型用作查询方法返回类型,如果它们满足以下条件:

类型实现Streamable.
该类型公开了一个构造函数或一个名为of(…)或作为参数的静态工厂方法。valueOf(…)Streamable
以下清单显示了一个示例:

class Product {
MonetaryAmount getPrice() { … }
}

@RequiredArgsConstructor(staticName = “of”)
class Products implements Streamable<Product> {

private final Streamable<Product> streamable;

public MonetaryAmount getTotal() {
return streamable.stream()
.map(Priced::getPrice)
.reduce(Money.of(0), MonetaryAmount::add);
}

@Override
public Iterator<Product> iterator() {
return streamable.iterator();
}
}

interface ProductRepository implements Repository<Product, Long> {
Products findAllByDescriptionContaining(String text);
}
公开 API 以访问产品价格的Product实体。

Streamable<Product>可以通过使用Products.of(…)(使用 Lombok 注释创建的工厂方法)构造的包装器类型。采用意志的标准构造函数Streamable<Product>也可以。

包装器类型公开了一个额外的 API,用于计算Streamable<Product>.

实现Streamable接口并委托给实际结果。

该包装器类型Products可以直接用作查询方法返回类型。您不需要Streamable<Product>在存储库客户端中的查询之后返回并手动包装它。

Spring认证:Spring Data Neo4j教程五
支持 Vavr 集合
Vavr是一个包含 Java 函数式编程概念的库。它附带一组自定义集合类型,您可以将其用作查询方法返回类型,如下表所示:

Spring认证:Spring Data Neo4j教程五
您可以将第一列(或其子类型)中的类型用作查询方法返回类型,并根据实际查询结果的Java类型(第三列)获取第二列中用作实现类型的类型。或者,您可以声明Traversable(VavrIterable等效项),然后我们从实际返回值派生实现类。也就是说,ajava.util.List变成 VavrList或Seq,ajava.util.Set变成 Vavr LinkedHashSet Set,依此类推。

回到顶部