使用 Spring Data Repositories(中)
发布于 3 年前 作者 rliu 3578 次浏览 来自 分享

原标题:Spring认证|使用 Spring Data Repositories(中)来源:(Spring中国教育管理中心)

4.4.5. 限制查询结果

您可以使用first或top关键字来限制查询方法的结果,这两个关键字可以互换使用。您可以将一个可选的数值附加到top或first指定要返回的最大结果大小。如果忽略该数字,则假定结果大小为 1。以下示例显示了如何限制查询大小:

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

User findFirstByOrderByLastnameAsc();

User findTopByOrderByAgeDesc();

Page queryFirst10ByLastname(String lastname, Pageable pageable);

Slice findTop3ByLastname(String lastname, Pageable pageable);

List findFirst10ByLastname(String lastname, Sort sort);

List findTop10ByLastname(String lastname, Pageable pageable);

限制表达式还支持Distinct支持不同查询的数据存储的关键字。此外,对于将结果集限制为一个实例的查询,Optional支持将结果用关键字包装。

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

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

4.4.6. 返回集合或可迭代对象的存储库方法

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

使用 Streamable 作为查询方法返回类型

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

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

interface PersonRepository extends Repository {

Streamable findByFirstnameContaining(String firstname);

Streamable findByLastnameContaining(String lastname);

}

Streamable result = repository.findByFirstnameContaining(“av”)

.and(repository.findByLastnameContaining(“ea”));

返回自定义流包装器类型

为集合提供专用包装器类型是一种常用模式,用于为返回多个元素的查询结果提供 API。通常,通过调用存储库方法返回类集合类型并手动创建包装器类型的实例来使用这些类型。您可以避免该额外步骤,因为 Spring Data 允许您将这些包装器类型用作查询方法返回类型,前提是它们满足以下条件:

类型实现Streamable.

的类型公开任一个构造或命名静态工厂法of(…)或valueOf(…)该取Streamable作为参数。

以下清单显示了一个示例:

class Product {

MonetaryAmount getPrice() { … }

}

@RequiredArgsConstructor(staticName = “of”)

class Products implements Streamable {

private final Streamable streamable;

public MonetaryAmount getTotal() {

return streamable.stream()

.map(Priced::getPrice)

.reduce(Money.of(0), MonetaryAmount::add);

}

@Override

public Iterator iterator() {

return streamable.iterator();

}

}

interface ProductRepository implements Repository {

Products findAllByDescriptionContaining(String text);

}

一个Product暴露的API来访问产品的价格实体。

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

包装器类型公开了一个额外的 API,在Streamable.

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

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

支持 Vavr 集合

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

您可以使用第一列(或其子类型)中的类型作为查询方法返回类型,并根据实际查询结果(第三列)的 Java 类型获取第二列中的类型作为实现类型。或者,您可以声明Traversable(Iterable相当于Vavr ),然后我们从实际返回值派生实现类。也就是说, ajava.util.List变成了 VavrList或Seq, ajava.util.Set变成了 Vavr LinkedHashSet Set,依此类推。

4.4.7. 存储库方法的空处理

从 Spring Data 2.0 开始,返回单个聚合实例的存储库 CRUD 方法使用 Java 8Optional来指示可能缺少值。除此之外,Spring Data 支持在查询方法上返回以下包装器类型:

com.google.common.base.Optional

scala.Option

io.vavr.control.Option

或者,查询方法可以选择根本不使用包装器类型。然后通过返回来指示不存在查询结果null。返回集合、集合替代、包装器和流的存储库方法保证永远不会返回null,而是返回相应的空表示。有关详细信息,请参阅“存储库查询返回类型”。

可空性注释

您可以使用Spring Framework 的可空性注释来表达存储库方法的可空性约束。它们null在运行时提供了一种工具友好的方法和选择加入检查,如下所示:

@NonNullApi: 在包级别上用于声明参数和返回值的默认行为分别是既不接受也不产生null值。

@NonNull: 用于不得为的参数或返回值null(在@NonNullApi适用的参数和返回值上不需要)。

@Nullable: 用于可以是的参数或返回值null。

Spring 注释使用JSR 305注释(一种休眠但广泛使用的 JSR)进行元注释。JSR 305 元注释让工具供应商(例如IDEA、Eclipse和Kotlin)以通用方式提供空安全支持,而无需对 Spring 注释进行硬编码支持。要为查询方法启用可空性约束的运行时检查,您需要使用 Spring 的@NonNullApiin在包级别激活非可空性package-info.java,如以下示例所示:

示例 22. 在 package-info.java

@org.springframework.lang.NonNullApi

package com.acme;

一旦非空默认设置到位,存储库查询方法调用将在运行时验证为可空性约束。如果查询结果违反了定义的约束,则抛出异常。当该方法将返回null但被声明为不可为空时(在存储库所在的包上定义的默认注释),就会发生这种情况。如果您想再次选择可空结果,请有选择地使用@Nullable单个方法。使用本节开头提到的结果包装器类型继续按预期工作:空结果被转换为表示不存在的值。

以下示例显示了刚刚描述的许多技术:

示例 23.使用不同的可空性约束

package com.acme;

import org.springframework.lang.Nullable;

interface UserRepository extends Repository {

User getByEmailAddress(EmailAddress emailAddress);

@Nullable

User findByEmailAddress(@Nullable EmailAddress emailAdress);

Optional findOptionalByEmailAddress(EmailAddress emailAddress);

}

存储库驻留在我们定义了非空行为的包(或子包)中。

EmptyResultDataAccessException当查询未产生结果时抛出。IllegalArgumentException当emailAddress传递给方法是时抛出null。

null当查询未产生结果时返回。也接受null作为 的值emailAddress。

Optional.empty()当查询未产生结果时返回。IllegalArgumentException当emailAddress传递给方法是时抛出null。

基于 Kotlin 的存储库中的可空性

Kotlin在语言中定义了可空性约束。Kotlin 代码编译为字节码,它不通过方法签名而是通过编译元数据来表达可空性约束。确保kotlin-reflect在您的项目中包含JAR 以启用对 Kotlin 的可空性约束的内省。Spring Data 存储库使用语言机制来定义这些约束以应用相同的运行时检查,如下所示:

示例 24.在 Kotlin 存储库上使用可空性约束

interface UserRepository : Repository {

fun findByUsername(username: String): User

fun findByFirstname(firstname: String?): User?

}

该方法将参数和结果都定义为不可为空(Kotlin 默认值)。Kotlin 编译器拒绝传递null给方法的方法调用。如果查询产生空结果,EmptyResultDataAccessException则抛出an 。

该方法接受null的firstname参数,并返回null,如果查询不产生结果。

4.4.8. 流式查询结果

您可以使用 Java 8Stream作为返回类型以增量方式处理查询方法的结果。不是将查询结果包装在 a 中Stream,而是使用数据存储特定的方法来执行流式传输,如以下示例所示:

示例 25. 使用 Java 8 流式传输查询结果 Stream

@Query(“select u from User u”)

Stream findAllByCustomQueryAndStream();

Stream readAllByFirstnameNotNull();

@Query(“select u from User u”)

Stream streamAllPaged(Pageable pageable);

AStream潜在地包装了底层数据存储特定的资源,因此必须在使用后关闭。您可以Stream使用close()方法或使用 Java 7try-with-resources块手动关闭,如以下示例所示:

示例 26.Stream在try-with-resources块中处理结果

try (Stream stream = repository.findAllByCustomQueryAndStream()) {

stream.forEach(…);

}

并非所有 Spring Data 模块当前都支持Stream作为返回类型。

4.4.9. 异步查询结果

您可以使用Spring 的异步方法运行能力异步运行存储库查询。这意味着该方法在调用时立即返回,而实际查询发生在已提交给 Spring 的任务中TaskExecutor。异步查询不同于反应式查询,不应混合使用。有关反应式支持的更多详细信息,请参阅商店特定的文档。以下示例显示了一些异步查询:

@Async

Future findByFirstname(String firstname);

@Async

CompletableFuture findOneByFirstname(String firstname);

@Async

ListenableFuture findOneByLastname(String lastname);

使用java.util.concurrent.Future作为返回类型。

使用 Java 8java.util.concurrent.CompletableFuture作为返回类型。

使用 aorg.springframework.util.concurrent.ListenableFuture作为返回类型。

4.5. 创建存储库实例

本节介绍如何为定义的存储库接口创建实例和 bean 定义。一种方法是使用支持存储库机制的每个 Spring Data 模块附带的 Spring 命名空间,尽管我们通常建议使用 Java 配置。

4.5.1. XML 配置

每个 Spring Data 模块都包含一个repositories元素,可让您定义 Spring 为您扫描的基本包,如以下示例所示:

示例 27. 通过 XML 启用 Spring Data 存储库

xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance

xmlns=“http://www.springframework.org/schema/data/jpa

xsi:schemaLocation="http://www.springframework.org/schema/beans

https://www.springframework.org/schema/beans/spring-beans.xsd

http://www.springframework.org/schema/data/jpa

https://www.springframework.org/schema/data/jpa/spring-jpa.xsd">

在前面的示例中,指示 Spring 扫描com.acme.repositories及其所有子包以查找扩展Repository的接口或其子接口之一。对于找到的每个接口,基础结构注册特定FactoryBean于持久性技术以创建处理查询方法调用的适当代理。每个 bean 都在从接口名称派生的 bean 名称下注册,因此 的接口UserRepository将在 下注册userRepository。嵌套存储库接口的 Bean 名称以其封闭的类型名称为前缀。该base-package属性允许使用通配符,以便您可以定义扫描包的模式。

使用过滤器

默认情况下,基础设施会选择每个接口,这些接口扩展Repository位于配置的基本包下的持久性技术特定的子接口,并为其创建一个 bean 实例。但是,您可能希望更精细地控制哪些接口为其创建了 bean 实例。为此,请在元素内使用和元素。语义完全等同于 Spring 上下文命名空间中的元素。有关详细信息,请参阅这些元素的Spring 参考文档。

例如,要将某些接口从实例化中排除为存储库 bean,您可以使用以下配置:

示例 28. 使用 exclude-filter 元素

前面的示例排除了所有以SomeRepository实例化结尾的接口。

内容来源:(Spring中国教育管理中心)

使用 Spring Data Repositories,未完待续…

回到顶部