原标题:Spring认证中国教育管理中心-Spring Data Couchbase教程五(Spring中国教育管理中心)
4.4.5限制查询结果
您可以使用firstortop关键字来限制查询方法的结果,您可以互换使用它们。您可以将可选数值附加到top或first指定要返回的最大结果大小。如果省略该数字,则假定结果大小为 1。以下示例显示了如何限制查询大小:
示例 39. 使用Topand限制查询的结果大小First
代码语言:javascript复制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”最大元素的查询方法。
4.4.6返回集合或迭代的存储库方法
查询方法,返回多个结果可以使用标准的Java Iterable,List和Set。除此之外,我们还支持返回 Spring Data 的Streamable自定义扩展Iterable,以及Vavr提供的集合类型。请参阅解释所有可能的查询方法返回类型的附录。
使用 Streamable 作为查询方法返回类型
您可以Streamable用作Iterable任何集合类型的替代品或任何集合类型。它提供了方便的方法来访问非并行Stream(缺少Iterable)以及直接….filter(…)和….map(…)覆盖元素并将其连接Streamable到其他元素的能力:
示例 40. 使用 Streamable 组合查询方法结果
代码语言:javascript复制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作为参数。
以下清单显示了一个示例:
代码语言:javascript复制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);
}
一个Product暴露的API来访问产品的价格实体。
Streamable<Product>可以通过使用Products.of(…)(使用 Lombok 注释创建的工厂方法)构造的包装器类型。采用Streamable<Product>意志的标准构造函数也可以。
包装器类型公开了一个额外的 API,用于计算Streamable<Product>.
实现Streamable接口并委托给实际结果。
该包装器类型Products可以直接用作查询方法返回类型。您不需要Streamable<Product>在存储库客户端中的查询之后返回并手动包装它。
支持 Vavr 集合
Vavr是一个包含 Java 函数式编程概念的库。它附带一组自定义集合类型,您可以将其用作查询方法返回类型,如下表所示:
您可以将第一列(或其子类型)中的类型用作查询方法返回类型,并根据实际查询结果的Java类型(第三列)获取第二列中用作实现类型的类型。或者,您可以声明Traversable(VavrIterable等效项),然后我们从实际返回值派生实现类。也就是说,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如下例所示:
示例 41. 声明不可为空 package-info.java
代码语言:javascript复制@org.springframework.lang.NonNullApi
package com.acme;
一旦非空默认设置到位,存储库查询方法调用将在运行时验证可空性约束。如果查询结果违反了定义的约束,则会引发异常。当方法将返回null但被声明为不可为空(默认情况下,在存储库所在的包上定义注释)时,就会发生这种情况。如果您想再次选择可空结果,请有选择地使用@Nullable单个方法。使用本节开头提到的结果包装类型继续按预期工作:空结果被转换为表示缺席的值。
以下示例显示了刚刚描述的一些技术:
示例 42. 使用不同的可空性约束
代码语言:javascript复制package com.acme;
import org.springframework.lang.Nullable;
interface UserRepository extends Repository<User, Long> {
User getByEmailAddress(EmailAddress emailAddress);
@Nullable
User findByEmailAddress(@Nullable EmailAddress emailAdress);
Optional<User> findOptionalByEmailAddress(EmailAddress emailAddress);
}
存储库位于我们为其定义了非空行为的包(或子包)中。
EmptyResultDataAccessException当查询不产生结果时抛出一个。IllegalArgumentException当emailAddress交给方法时抛出一个null。
null当查询没有产生结果时返回。也接受null作为 的值emailAddress。
Optional.empty()当查询没有产生结果时返回。IllegalArgumentException当emailAddress交给方法时抛出一个null。
基于 Kotlin 的存储库中的可空性
Kotlin 将可空性约束的定义融入到语言中。Kotlin 代码编译为字节码,它不通过方法签名表达可空性约束,而是通过编译的元数据。确保kotlin-reflect在您的项目中包含JAR 以启用对 Kotlin 可空性约束的自省。Spring Data 存储库使用语言机制来定义这些约束以应用相同的运行时检查,如下所示:
示例 43. 在 Kotlin 存储库上使用可空性约束
代码语言:javascript复制interface UserRepository : Repository<User, String> {
fun findByUsername(username: String): User
fun findByFirstname(firstname: String?): User?
}
该方法将参数和结果都定义为不可为空(Kotlin 默认)。Kotlin 编译器拒绝传递null给方法的方法调用。如果查询产生空结果, EmptyResultDataAccessException则抛出an 。
该方法接受null的firstname参数,并返回null,如果查询不产生结果。
4.4.8流式查询结果
您可以使用 Java 8Stream<T>作为返回类型以增量方式处理查询方法的结果。不是将查询结果包装在 a 中Stream,而是使用特定于数据存储的方法来执行流式传输,如以下示例所示:
示例 44. 使用 Java 8 流式传输查询结果 Stream<T>
代码语言:javascript复制@Query("select u from User u")
Stream<User> findAllByCustomQueryAndStream();
Stream<User> readAllByFirstnameNotNull();
@Query("select u from User u")
Stream<User> streamAllPaged(Pageable pageable);
AStream可能包装底层数据存储特定的资源,因此必须在使用后关闭。您可以Stream使用该close()方法或使用 Java 7try-with-resources块手动关闭,如以下示例所示:
示例 45. 处理块中的Stream<T>结果try-with-resources
代码语言:javascript复制try (Stream<User> stream = repository.findAllByCustomQueryAndStream()) {
stream.forEach(…);
}
并非所有 Spring Data 模块当前都支持Stream<T>作为返回类型。
4.4.9异步查询结果
您可以使用Spring 的异步方法运行能力异步运行存储库查询。这意味着该方法在调用时立即返回,而实际查询发生在已提交给 Spring 的任务中TaskExecutor。异步查询与响应式查询不同,不应混用。有关响应式支持的更多详细信息,请参阅特定于商店的文档。以下示例显示了许多异步查询:
代码语言:javascript复制@Async
Future<User> findByFirstname(String firstname);
@Async
CompletableFuture<User> findOneByFirstname(String firstname);
@Async
ListenableFuture<User> 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 为您扫描的基本包,如以下示例所示:
示例 46. 通过 XML 启用 Spring Data 存储库
代码语言:javascript复制<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns:beans="http://www.springframework.org/schema/beans"
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">
<repositories base-package="com.acme.repositories" />
</beans:beans>
在前面的示例中,指示 Spring 扫描com.acme.repositories其所有子包以查找扩展接口Repository或其子接口之一。对于找到的每个接口,基础设施都会注册特定的持久性技术,FactoryBean以创建适当的代理来处理查询方法的调用。每个 bean 都在从接口名称派生的 bean 名称下注册,因此接口UserRepository将在userRepository. 嵌套存储库接口的 Bean 名称以它们的封闭类型名称为前缀。该base-package属性允许使用通配符,以便您可以定义扫描包的模式。
使用过滤器
默认情况下,基础设施会选择扩展Repository位于已配置基本包下的特定于持久性技术的子接口的每个接口,并为其创建一个 bean 实例。但是,您可能希望更细粒度地控制哪些接口具有为其创建的 bean 实例。为此,请在元素内使用<include-filter />和<exclude-filter />元素<repositories />。语义完全等同于 Spring 上下文命名空间中的元素。有关详细信息,请参阅这些元素的Spring 参考文档。
例如,要从实例化中排除某些接口作为存储库 bean,您可以使用以下配置:
示例 47. 使用 exclude-filter 元素
代码语言:javascript复制<repositories base-package="com.acme.repositories">
<context:exclude-filter type="regex" expression=".*SomeRepository" />
</repositories>
前面的示例排除了所有以SomeRepository实例化结尾的接口。
4.5.2.Java 配置
您还可以通过@Enable${store}Repositories在 Java 配置类上使用特定于存储的注释来触发存储库基础结构。有关 Spring 容器的基于 Java 的配置的介绍,请参阅Spring 参考文档中的 JavaConfig。
启用 Spring Data 存储库的示例配置类似于以下内容:
示例 48.基于注释的存储库配置示例
代码语言:javascript复制@Configuration
@EnableJpaRepositories("com.acme.repositories")
class ApplicationConfiguration {
@Bean
EntityManagerFactory entityManagerFactory() {
// …
}
}
前面的示例使用特定于 JPA 的注释,您可以根据实际使用的存储模块对其进行更改。这同样适用于EntityManagerFactorybean的定义。请参阅涵盖商店特定配置的部分。
4.5.3.独立使用
您还可以在 Spring 容器之外使用存储库基础架构——例如,在 CDI 环境中。您的类路径中仍然需要一些 Spring 库,但通常您也可以通过编程方式设置存储库。提供存储库支持的 Spring Data 模块附带了特定RepositoryFactory于您可以使用的持久性技术,如下所示:
示例 49. 存储库工厂的独立使用
代码语言:javascript复制RepositoryFactorySupport factory = … // Instantiate factory here
UserRepository repository = factory.getRepository(UserRepository.class);