Spring Data(数据) LDAP

时间:2022-11-21 18:10:39

版本 3.0.0

Spring Data LDAP使构建使用轻量级目录访问协议(LDAP)的基于Spring的应用程序变得更加容易。

本文档是 Spring 数据的参考指南 - 文档支持。它解释了文档模块的概念和语义以及各种数据存储命名空间的语法。

Spring Data(数据) LDAP

1. 认识春天

Spring Data 使用 Spring Framework的核心功能,包括:

  • 国际奥委会容器
  • 验证、类型转换和数据绑定
  • 表达式语言
  • AOP
  • JMX 集成
  • 道支持
  • DAO 异常层次结构

虽然了解Spring API并不重要,但了解它们背后的概念很重要。至少,这个想法 无论您选择使用哪种 IoC 容器,都应该熟悉 IoC 后面。

LDAP支持的核心功能可以直接使用,无需调用IoC服务 的弹簧容器。这很像,它可以“独立”使用,无需任何其他服务 的弹簧容器。要使用 Spring Data LDAP 的所有功能,例如存储库支持, 您必须使用 Spring 配置库的某些部分。​​JdbcTemplate​

要了解有关 Spring 的更多信息,您可以参考详细解释 Spring 框架的综合文档。有很多关于春天的文章、博客条目和书籍。 有关更多信息,请参阅 Spring 框架主页。

虽然了解Spring API并不重要,但您确实需要了解它们背后的概念。至少,对于您选择使用的任何 IoC 容器,都应该熟悉 IoC 背后的理念。

要了解有关 Spring 的更多信息,您可以参考详细解释 Spring 框架的综合文档。你可以在Spring上找到很多文章、博客条目和书籍。有关更多信息,请参阅 Spring 框架主页。

2. 要求

Spring Data LDAP 2.x 二进制文件需要 JDK 8.0 或更高版本、Spring Framework6.0.0 或更高版本以及Spring LDAP3.0.0 或更高版本。

3. 其他帮助资源

学习一个新框架并不总是直截了当的。在本节中,我们尝试提供我们认为易于遵循的指南,以便从Spring Data LDAP模块开始。但是,如果您遇到问题或正在寻求建议,请尝试以下一个或多个资源:

社区论坛

* 上的 Spring DataStack Overflow是所有 Spring Data(不仅仅是文档)用户共享信息和互相帮助的标签。请注意,只有发布时才需要注册。

专业支持

专业的,从源头支持,保证响应时间,可从Spring and Spring Data背后的公司Pivotal Sofware,Inc.获得。

3.1. 后续开发

有关Spring Data LDAP源代码存储库,夜间构建和快照工件的信息,请参阅Spring Data LDAP主页。您可以通过*上的社区与开发人员进行交互,从而帮助Spring Data最好地满足Spring社区的需求。要跟踪开发人员活动,请在Spring Data LDAP主页上查找邮件列表信息。如果您遇到错误或想提出改进建议,请在 Spring 数据问题跟踪器上创建工单。要及时了解Spring生态系统中的最新消息和公告,请订阅Spring社区门户。最后,您可以在Twitter(SpringData)上关注Spring博客或项目团队。

3.2. 项目元数据

  • 版本控制:https://github.com/spring-projects/spring-data-ldap
  • 错误跟踪器:https://github.com/spring-projects/spring-data-ldap/issues
  • 发布库:https://repo.spring.io/libs-release
  • 里程碑存储库:https://repo.spring.io/libs-milestone
  • 快照存储库:https://repo.spring.io/libs-snapshot:级别偏移:+1

升级春季数据

有关如何从早期版本的 Spring 数据升级的说明在项目wiki 上提供。 按照发行说明部分中的链接查找要升级到的版本。

升级说明始终是发行说明中的第一项。如果您落后多个版本,请确保您还查看了您跳转的版本的发行说明。

4. 依赖关系

由于各个 Spring 数据模块的开始日期不同,因此它们中的大多数都带有不同的主要和次要版本号。找到兼容版本的最简单方法是依靠我们随附的与定义的兼容版本一起提供的春季数据发布列车 BOM。在 Maven 项目中,您将在 POM 的部分中声明此依赖项,如下所示:​​<dependencyManagement />​

例 1.使用弹簧数据发布列车物料清单

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-bom</artifactId>
<version>2022.0.0</version>
<scope>import</scope>
<type>pom</type>
</dependency>
</dependencies>
</dependencyManagement>

当前发布训练版本是。火车版本使用带有图案的犊牛。 对于 GA 版本和服务版本,版本名称如下,对于所有其他版本,版本名称如下:,其中可以是以下之一:​​2022.0.0​​​​YYYY.MINOR.MICRO​​​​${calver}​​​​${calver}-${modifier}​​​​modifier​

  • ​SNAPSHOT​​:当前快照
  • ​M1​​,,等等:里程碑M2
  • ​RC1​​,,等等:发布候选版本RC2

您可以在我们的Spring 数据示例存储库中找到使用 BOM 的工作示例。有了这个,你可以声明你想要使用的 Spring 数据模块,而无需在块中有一个版本,如下所示:​​<dependencies />​

例 2.声明对 Spring 数据模块的依赖关系

<dependencies>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
</dependency>
<dependencies>

4.1. 使用 Spring 引导进行依赖管理

Spring Boot 会为你选择最新版本的 Spring 数据模块。如果仍要升级到较新版本,请将 要使用的训练版本和迭代的属性。​​spring-data-releasetrain.version​

4.2. 弹簧框架

Spring 数据模块的当前版本需要 Spring Framework 6.0.0 或更高版本。这些模块还可以使用该次要版本的较旧错误修复版本。但是,强烈建议使用该代中的最新版本。

5. 使用 Spring 数据存储库

Spring 数据存储库抽象的目标是显著减少为各种持久性存储实现数据访问层所需的样板代码量。


Spring 数据存储库文档和您的模块



本章解释了 Spring 数据存储库的核心概念和接口。 本章中的信息来自 Spring 数据共享模块。 它使用 Jakarta 持久性 API (JPA) 模块的配置和代码示例。 如果要使用 XML 配置,则应调整 XML 命名空间声明和要扩展的类型,以使用的特定模块的等效项。“命名空间参考​”涵盖了XML配置,所有支持存储库API的Spring Data模块都支持XML配置。 “存储库查询关键字”涵盖了存储库抽象通常支持的查询方法关键字。 有关模块特定功能的详细信息,请参阅本文档有关该模块的章节。


5.1. 核心概念

Spring 数据存储库抽象中的中心接口是。 它采用要管理的域类以及域类的 ID 类型作为类型参数。 此接口主要充当标记接口,用于捕获要使用的类型,并帮助您发现扩展此接口的接口。 CrudRepository 和ListCrudRepository接口为正在管理的实体类提供了复杂的 CRUD 功能。​​Repository​

示例 3.界面​​CrudRepository​

public interface CrudRepository<T, ID> extends Repository<T, ID> {

<S extends T> S save(S entity);

Optional<T> findById(ID primaryKey);

Iterable<T> findAll();

long count();

void delete(T entity);

boolean existsById(ID primaryKey);

// … more functionality omitted.
}

保存给定的实体。

返回由给定 ID 标识的实体。

返回所有实体。

返回实体数。

删除给定实体。

指示具有给定 ID 的实体是否存在。

​ListCrudRepository​​提供等效的方法,但它们返回方法返回 an。​​List​​​​CrudRepository​​​​Iterable​

我们还提供特定于持久性技术的抽象,例如 asor。 这些接口扩展并公开了底层持久性技术的功能,以及相当通用的持久性技术无关的接口,例如。​​JpaRepository​​​​MongoRepository​​​​CrudRepository​​​​CrudRepository​

除此之外,还有一个PagingAndSortingRepository抽象,它添加了其他方法来简化对实体的分页访问:​​CrudRepository​

例 4.接口​​PagingAndSortingRepository​

public interface PagingAndSortingRepository<T, ID>  {

Iterable<T> findAll(Sort sort);

Page<T> findAll(Pageable pageable);
}

要访问页面大小为 20 的第二页,您可以执行以下操作:​​User​

PagingAndSortingRepository<User, Long> repository = // … get access to a bean
Page<User> users = repository.findAll(PageRequest.of(1, 20));

除了查询方法之外,还可以对计数查询和删除查询进行查询派生。 以下列表显示了派生计数查询的接口定义:

例 5.派生计数查询

interface UserRepository extends CrudRepository<User, Long> {

long countByLastname(String lastname);
}

以下清单显示了派生删除查询的接口定义:

例 6.派生删除查询

interface UserRepository extends CrudRepository<User, Long> {

long deleteByLastname(String lastname);

List<User> removeByLastname(String lastname);
}

5.2. 查询方法

标准 CRUD 功能存储库通常对基础数据存储具有查询。 使用 Spring Data,声明这些查询变成了一个四步过程:

  1. 声明扩展存储库或其子接口之一的接口,并将其键入应处理的域类和 ID 类型,如以下示例所示:

interface PersonRepository extends Repository<Person, Long> { … }
  1. 在接口上声明查询方法。
interface PersonRepository extends Repository<Person, Long> {
List<Person> findByLastname(String lastname);
}
  1. 设置 Spring 以使用JavaConfig或XML 配置为这些接口创建代理实例。

    爪哇岛
    .XML
@EnableLdapRepositories
class Config { … }

此示例中使用 JPA 命名空间。 如果将存储库抽象用于任何其他存储,则需要将其更改为存储模块的相应命名空间声明。 换句话说,您应该交换支持,例如,。jpamongodb

请注意,JavaConfig 变体不会显式配置包,因为缺省情况下使用带注释的类的包。 要自定义要扫描的包,请使用特定于数据存储的存储库注释的属性之一。basePackage…@EnableLdapRepositories

  1. 注入存储库实例并使用它,如以下示例所示:
class SomeClient {

private final PersonRepository repository;

SomeClient(PersonRepository repository) {
this.repository = repository;
}

void doSomething() {
List<Person> persons = repository.findByLastname("Matthews");
}
}

以下各节详细介绍了每个步骤:

  • 定义存储库接口
  • 定义查询方法
  • 创建存储库实例
  • Spring 数据存储库的自定义实现

5.3. 定义存储库接口

要定义存储库接口,首先需要定义特定于域类的存储库接口。 接口必须扩展并键入域类和 ID 类型。 如果要公开该域类型的 CRUD 方法,可以扩展或其变体之一,而不是。​​Repository​​​​CrudRepository​​​​Repository​

5.3.1. 微调存储库定义

您可以通过几种变体开始使用存储库界面。

典型的方法是扩展,这为您提供了 CRUD 功能的方法。 CRUD 代表 创建、读取、更新、删除。 在 3.0 版中,我们还引入了这与 但是对于那些返回多个实体的方法,它返回的是您可能发现更容易使用的方法。​​CrudRepository​​​​ListCrudRepository​​​​CrudRepository​​​​List​​​​Iterable​

如果您使用的是反应式存储,则可以选择,或者取决于您使用的反应式框架。​​ReactiveCrudRepository​​​​RxJava3CrudRepository​

如果你正在使用 Kotlin,你可以选择哪个利用 Kotlin 的协程。​​CoroutineCrudRepository​

此外,您还可以扩展,,,或者如果您需要允许指定抽象或在第一种情况下指定抽象的方法。 请注意,各种排序存储库不再像在 Spring 数据版本 3.0 之前那样扩展其各自的 CRUD 存储库。 因此,如果需要这两个接口的功能,则需要扩展这两个接口。​​PagingAndSortingRepository​​​​ReactiveSortingRepository​​​​RxJava3SortingRepository​​​​CoroutineSortingRepository​​​​Sort​​​​Pageable​

如果您不想扩展 Spring 数据接口,您还可以使用注释存储库接口。 扩展其中一个 CRUD 存储库接口会公开一组完整的方法来操作实体。 如果您希望对要公开的方法有选择性,请将要公开的方法从 CRUD 存储库复制到域存储库中。 执行此操作时,可以更改方法的返回类型。 如果可能,Spring 数据将遵循返回类型。 例如,对于返回多个实体的方法,您可以选择 VAVR 列表。​​@RepositoryDefinition​​​​Iterable<T>​​​​List<T>​​​​Collection<T>​

如果应用程序中的许多存储库应具有相同的方法集,则可以定义自己的基本接口进行继承。 必须对这样的接口进行注释。 这可以防止 Spring Data 尝试直接创建它的实例并失败,因为它无法确定该存储库的实体,因为它仍然包含一个通用类型变量。​​@NoRepositoryBean​

下面的示例演示如何有选择地公开 CRUD 方法(在本例中为):​​findById​​​​save​

例 7.有选择地公开 CRUD 方法

@NoRepositoryBean
interface MyBaseRepository<T, ID> extends Repository<T, ID> {

Optional<T> findById(ID id);

<S extends T> S save(S entity);
}

interface UserRepository extends MyBaseRepository<User, Long> {
User findByEmailAddress(EmailAddress emailAddress);
}

在前面的示例中,您为所有域存储库和公开以及定义了通用基本接口。这些方法被路由到 Spring Data 提供的您选择的存储的基本存储库实现中(例如,如果您使用 JPA,则实现是),因为它们与方法签名匹配。 因此,现在可以保存用户,按ID查找单个用户,并触发查询以按电子邮件地址查找。​​findById(…)​​​​save(…)​​​​SimpleJpaRepository​​​​CrudRepository​​​​UserRepository​​​​Users​

中间存储库接口带有注释。 确保将该注释添加到 Spring Data 在运行时不应为其创建实例的所有存储库接口。​​@NoRepositoryBean​

5.3.2. 使用具有多个 Spring 数据模块的存储库

在应用程序中使用唯一的 Spring 数据模块使事情变得简单,因为定义范围内的所有存储库接口都绑定到 Spring 数据模块。 有时,应用程序需要使用多个 Spring 数据模块。 在这种情况下,存储库定义必须区分持久性技术。 当它在类路径上检测到多个存储库工厂时,Spring Data 进入严格的存储库配置模式。 严格配置使用存储库或域类的详细信息来决定存储库定义的 Spring 数据模块绑定:

  1. 如果存储库定义扩展了特定于模块的存储库,则它是特定 Spring 数据模块的有效候选者。
  2. 如果域类使用特定于模块的类型注释进行注释,则它是特定 Spring 数据模块的有效候选者。 Spring Data 模块接受第三方注释(例如 JPA)或提供自己的注释(例如 Spring Data MongoDB 和 Spring Data Elasticsearch)。@Entity@Document

以下示例显示了使用特定于模块的接口(在本例中为 JPA)的存储库:

例 8.使用特定于模块的接口的存储库定义

interface MyRepository extends JpaRepository<User, Long> { }

@NoRepositoryBean
interface MyBaseRepository<T, ID> extends JpaRepository<T, ID> { … }

interface UserRepository extends MyBaseRepository<User, Long> { … }

​MyRepository​​并扩展其类型层次结构。 它们是 Spring Data JPA 模块的有效候选者。​​UserRepository​​​​JpaRepository​

以下示例显示了使用通用接口的存储库:

例 9.使用通用接口的存储库定义

interface AmbiguousRepository extends Repository<User, Long> { … }

@NoRepositoryBean
interface MyBaseRepository<T, ID> extends CrudRepository<T, ID> { … }

interface AmbiguousUserRepository extends MyBaseRepository<User, Long> { … }

​AmbiguousRepository​​并仅在其类型层次结构中扩展。 虽然这在使用唯一的 Spring 数据模块时很好,但多个模块无法区分这些存储库应该绑定到哪个特定的 Spring 数据。​​AmbiguousUserRepository​​​​Repository​​​​CrudRepository​

以下示例显示了一个使用带有注释的域类的存储库:

例 10.使用带有注释的域类的存储库定义

interface PersonRepository extends Repository<Person, Long> { … }

@Entity
class Person { … }

interface UserRepository extends Repository<User, Long> { … }

@Document
class User { … }

​PersonRepository​​引用,它用JPAannotation注释,所以这个存储库显然属于Spring Data JPA.references,它用Spring Data MongoDB的sannotation注释。​​Person​​​​@Entity​​​​UserRepository​​​​User​​​​@Document​

以下错误示例显示了一个使用具有混合注释的域类的存储库:

例 11.使用具有混合注释的域类的存储库定义

interface JpaPersonRepository extends Repository<Person, Long> { … }

interface MongoDBPersonRepository extends Repository<Person, Long> { … }

@Entity
@Document
class Person { … }

这个例子展示了一个同时使用JPA和Spring Data MongoDB注释的域类。 它定义了两个存储库,并且。 一个用于JPA,另一个用于MongoDB使用。 Spring 数据不再能够区分存储库,这会导致未定义的行为。​​JpaPersonRepository​​​​MongoDBPersonRepository​

存储库类型详细信息和区分域类注释用于严格的存储库配置,以识别特定 Spring 数据模块的存储库候选者。 可以对同一域类型使用多个特定于持久性技术的注释,并允许跨多个持久性技术重用域类型。 但是,Spring Data 无法再确定绑定存储库的唯一模块。

区分存储库的最后一种方法是确定存储库基础包的范围。 基本包定义扫描存储库接口定义的起点,这意味着存储库定义位于相应的包中。 默认情况下,注释驱动的配置使用配置类的包。 基于 XML 的配置中的基本包是必需的。

以下示例显示了基本包的注释驱动配置:

例 12.注释驱动的基本包配置

@EnableJpaRepositories(basePackages = "com.acme.repositories.jpa")
@EnableMongoRepositories(basePackages = "com.acme.repositories.mongo")
class Configuration { … }

5.4. 定义查询方法

存储库代理有两种方法可以从方法名称派生特定于存储的查询:

  • 通过直接从方法名称派生查询。
  • 通过使用手动定义的查询。

可用选项取决于实际商店。 但是,必须有一个策略来决定创建什么实际查询。 下一节介绍可用选项。

5.4.1. 查询查找策略

存储库基础结构可以使用以下策略来解析查询。 使用 XML 配置,您可以通过属性在命名空间中配置策略。 对于 Java 配置,您可以使用注释的属性。 特定数据存储可能不支持某些策略。​​query-lookup-strategy​​​​queryLookupStrategy​​​​EnableLdapRepositories​

  • ​CREATE​​尝试从查询方法名称构造特定于存储的查询。 一般方法是从方法名称中删除一组给定的已知前缀,并分析方法的其余部分。 您可以在“查询创建”中阅读有关查询构造的更多信息。
  • ​USE_DECLARED_QUERY​​尝试查找已声明的查询,如果找不到查询,则会引发异常。 查询可以通过某处的注释定义,也可以通过其他方式声明。 请参阅特定商店的文档以查找该商店的可用选项。 如果存储库基础结构在引导时找不到该方法的声明查询,则会失败。
  • ​CREATE_IF_NOT_FOUND​​(默认值)组合沙。 它首先查找已声明的查询,如果未找到已声明的查询,则会创建一个基于名称的自定义方法查询。 这是默认的查找策略,因此,如果未显式配置任何内容,则使用此方法。 它允许按方法名称快速定义查询,但也允许根据需要引入声明的查询来自定义调整这些查询。CREATEUSE_DECLARED_QUERY

5.4.2. 查询创建

Spring 数据存储库基础结构中内置的查询生成器机制对于构建对存储库实体的约束查询非常有用。

下面的示例演示如何创建多个查询:

例 13.从方法名称创建查询

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);
}

分析查询方法名称分为主语和谓语。 第一部分 (,) 定义查询的主题,第二部分构成谓词。 引言子句(主语)可以包含进一步的表达式。 (或其他引入关键字)之间的任何文本都被视为描述性的,除非使用结果限制关键字之一,例如 ato 在要创建的查询上设置不同的标志或Top/First以限制查询结果。find…Byexists…ByfindByDistinct

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

分析方法的实际结果取决于为其创建查询的暂留存储。 但是,有一些一般事项需要注意:

  • 表达式通常是属性遍历与可以连接的运算符相结合。 可以将属性表达式与 and 组合在一起。 您还可以获得对运算符的支持,例如,,, 和属性表达式。 支持的运算符可能因数据存储而异,因此请参阅参考文档的相应部分。ANDORBetweenLessThanGreaterThanLike
  • 方法解析器支持为单个属性(例如)或支持忽略大小写的类型的所有属性(通常是实例 — 例如,)设置 anflag。 是否支持忽略案例可能因商店而异,因此请参阅特定于商店的查询方法的参考文档中的相关部分。IgnoreCasefindByLastnameIgnoreCase(…)StringfindByLastnameAndFirstnameAllIgnoreCase(…)
  • 可以通过将 anclause 追加到引用属性的查询方法并提供排序方向 (or) 来应用静态排序。 要创建支持动态排序的查询方法,请参阅 “特殊参数处理”。OrderByAscDesc

5.4.3. 属性表达式

属性表达式只能引用托管实体的直接属性,如前面的示例所示。 在创建查询时,已确保分析的属性是托管域类的属性。 但是,也可以通过遍历嵌套属性来定义约束。 请考虑以下方法签名:

List<Person> findByAddressZipCode(ZipCode zipCode);

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

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

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

List<Person> findByAddress_ZipCode(ZipCode zipCode);

由于我们将下划线字符视为保留字符,因此强烈建议遵循标准的 Java 命名约定(即,不要在属性名称中使用下划线,而是使用驼峰大小写)。

5.4.4. 特殊参数处理

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

例 14。使用 、 和 in 查询方法​​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()​​​​Pageable.unpaged()​

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

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

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

分页和排序

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

例 15。定义排序表达式

Sort sort = Sort.by("firstname").ascending()
.and(Sort.by("lastname").descending());

有关定义排序表达式的更类型安全的方法,请从定义排序表达式的类型开始,并使用方法引用定义要排序的属性。

例 16。使用类型安全的 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 本机等工具时可能会干扰本机映像编译。

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

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

QSort sort = QSort.by(QPerson.firstname.asc())
.and(QSort.by(QPerson.lastname.desc()));

5.4.5. 限制查询结果

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

例 18。限制查询的结果大小​​Top​​​​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);

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

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

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

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

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

使用可流式处理作为查询方法返回类型

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

例 19。使用可流式处理合并查询方法结果

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"));
返回自定义可流式传输包装器类型

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

  1. 类型实现。Streamable
  2. 该类型公开构造函数或名为 dorthat 的静态工厂方法作为参数。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(…)​​​​Streamable<Product>​

包装器类型公开一个额外的 API,计算新值。​​Streamable<Product>​

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

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

支持 Vavr 集合

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

Vavr collection type

Used Vavr implementation type

Valid Java source types

​io.vavr.collection.Seq​

​io.vavr.collection.List​

​java.util.Iterable​

​io.vavr.collection.Set​

​io.vavr.collection.LinkedHashSet​

​java.util.Iterable​

​io.vavr.collection.Map​

​io.vavr.collection.LinkedHashMap​

​java.util.Map​

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

5.4.7. 仓库方法的空处理

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

  • ​com.google.common.base.Optional​
  • ​scala.Option​
  • ​io.vavr.control.Option​

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

可为空性注释

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

  • @NonNullApi:在包级别用于声明参数和返回值的默认行为分别是既不接受也不生成值。null
  • @NonNull:用于不得使用的参数或返回值(在适用的参数和返回值上不需要)。null@NonNullApi
  • @Nullable:用于可以的参数或返回值。null

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

例 20。声明 中的非可空性​​package-info.java​

@org.springframework.lang.NonNullApi
package com.acme;

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

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

例 21。使用不同的可为空性约束

package com.acme;                                                       

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

例 22。在 Kotlin 存储库上使用可空性约束

interface UserRepository : Repository<User, String> {

fun findByUsername(username: String): User

fun findByFirstname(firstname: String?): User?
}

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

此方法接受参数并返回查询未产生结果。​​null​​​​firstname​​​​null​

5.4.8. 流式查询结果

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

例 23。使用 Java 8 流式传输查询结果​​Stream<T>​

@Query("select u from User u")
Stream<User> findAllByCustomQueryAndStream();

Stream<User> readAllByFirstnameNotNull();

@Query("select u from User u")
Stream<User> streamAllPaged(Pageable pageable);

可能会包装特定于基础数据存储的资源,因此必须在使用后关闭。 您可以使用该方法或使用 Java 7block 手动关闭,如以下示例所示:​​Stream​​​​Stream​​​​close()​​​​try-with-resources​

例 24。在块中使用结果​​Stream<T>​​​​try-with-resources​

try (Stream<User> stream = repository.findAllByCustomQueryAndStream()) {
stream.forEach(…);
}

并非所有 Spring 数据模块当前都支持返回类型。​​Stream<T>​

5.4.9. 异步查询结果

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

@Async
Future<User> findByFirstname(String firstname);

@Async
CompletableFuture<User> findOneByFirstname(String firstname);

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

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

5.5. 创建存储库实例

本节介绍如何为定义的存储库接口创建实例和 Bean 定义。

5.5.1. Java 配置

使用 Java 配置类上的特定于存储的注释来定义存储库激活的配置。 有关 Spring 容器的基于 Java 的配置的介绍,请参阅Spring 参考文档中的 JavaConfig。​​@EnableLdapRepositories​

启用 Spring 数据存储库的示例配置类似于以下内容:

例 25。基于注释的存储库配置示例

@Configuration
@EnableJpaRepositories("com.acme.repositories")
class ApplicationConfiguration {

@Bean
EntityManagerFactory entityManagerFactory() {
// …
}
}

前面的示例使用特定于 JPA 的注释,您将根据实际使用的存储模块对其进行更改。这同样适用于thebean的定义。请参阅涵盖特定于商店的配置的部分。​​EntityManagerFactory​

5.5.2.XML 配置

每个 Spring 数据模块都包含一个元素,该元素允许您定义 Spring 为您扫描的基本包,如以下示例所示:​​repositories​

例 26。通过XML启用Spring 数据存储库

<?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">

<jpa:repositories base-package="com.acme.repositories" />

</beans:beans>

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

5.5.3. 使用过滤器

缺省情况下,基础结构选取扩展位于配置的基础包下的特定于持久性技术的子接口的每个接口,并为其创建一个 Bean 实例。 但是,您可能希望更精细地控制哪些接口为其创建了 Bean 实例。 为此,请在存储库声明中使用过滤器元素。 语义与 Spring 组件过滤器中的元素完全相同。 有关详细信息,请参阅这些元素的Spring 参考文档。​​Repository​

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

例 27。使用过滤器

爪哇岛

.XML

@Configuration
@EnableLdapRepositories(basePackages = "com.acme.repositories",
includeFilters = { @Filter(type = FilterType.REGEX, pattern = ".*SomeRepository") },
excludeFilters = { @Filter(type = FilterType.REGEX, pattern = ".*SomeOtherRepository") })
class ApplicationConfiguration {

@Bean
EntityManagerFactory entityManagerFactory() {
// …
}
}

前面的示例排除了以实例化结尾的所有接口,并包括以 结尾的接口。​​SomeRepository​​​​SomeOtherRepository​

5.5.4. 独立使用

您还可以在 Spring 容器之外使用存储库基础架构,例如,在 CDI 环境中。你的类路径中仍然需要一些 Spring 库,但通常,你也可以以编程方式设置存储库。提供存储库支持的 Spring 数据模块附带了您可以使用的特定于持久性技术的模块,如下所示:​​RepositoryFactory​

例 28。存储库工厂的独立使用

RepositoryFactorySupport factory = … // Instantiate factory here
UserRepository repository = factory.getRepository(UserRepository.class);

5.6. Spring 数据存储库的自定义实现

Spring Data 提供了各种选项来创建查询方法,只需很少的编码。 但是,当这些选项不符合您的需求时,您还可以为存储库方法提供自己的自定义实现。 本节介绍如何执行此操作。

5.6.1. 自定义单个仓库

要使用自定义功能丰富存储库,您必须首先定义片段接口和自定义功能的实现,如下所示:

例 29。自定义存储库功能的界面

interface CustomizedUserRepository {
void someCustomMethod(User user);
}

例 30。实现自定义存储库功能

class CustomizedUserRepositoryImpl implements CustomizedUserRepository {

public void someCustomMethod(User user) {
// Your custom implementation
}
}

与片段接口对应的类名中最重要的部分是后缀。​​Impl​

实现本身不依赖于 Spring 数据,可以是常规的 Spring bean。 因此,您可以使用标准的依赖注入行为来注入对其他 Bean 的引用(例如 a)、参与方面等。​​JdbcTemplate​

然后,您可以让存储库接口扩展片段接口,如下所示:

例 31。对存储库界面的更改

interface UserRepository extends CrudRepository<User, Long>, CustomizedUserRepository {

// Declare query methods here
}

使用存储库接口扩展片段接口结合了 CRUD 和自定义功能,并使其可供客户端使用。

Spring 数据存储库是通过使用形成存储库组合的片段来实现的。 片段是基本存储库、功能方面(如QueryDsl)和自定义接口及其实现。 每次向存储库界面添加接口时,都会通过添加片段来增强组合。 基本存储库和存储库方面实现由每个 Spring 数据模块提供。

以下示例显示了自定义接口及其实现:

例 32。片段及其实现

interface HumanRepository {
void someHumanMethod(User user);
}

class HumanRepositoryImpl implements HumanRepository {

public void someHumanMethod(User user) {
// Your custom implementation
}
}

interface ContactRepository {

void someContactMethod(User user);

User anotherContactMethod(User user);
}

class ContactRepositoryImpl implements ContactRepository {

public void someContactMethod(User user) {
// Your custom implementation
}

public User anotherContactMethod(User user) {
// Your custom implementation
}
}

以下示例显示了扩展的自定义存储库的接口:​​CrudRepository​

例 33。对存储库界面的更改

interface UserRepository extends CrudRepository<User, Long>, HumanRepository, ContactRepository {

// Declare query methods here
}

存储库可以由多个自定义实现组成,这些实现按其声明顺序导入。 自定义实现的优先级高于基本实现和存储库方面。 此排序允许您覆盖基本存储库和方面方法,并在两个片段提供相同的方法签名时解决歧义。 存储库片段不限于在单个存储库界面中使用。 多个存储库可以使用片段界面,允许您跨不同存储库重用自定义项。

以下示例显示了存储库片段及其实现:

例 34。片段覆盖​​save(…)​

interface CustomizedSave<T> {
<S extends T> S save(S entity);
}

class CustomizedSaveImpl<T> implements CustomizedSave<T> {

public <S extends T> S save(S entity) {
// Your custom implementation
}
}

以下示例显示了使用上述存储库片段的存储库:

例 35。自定义存储库接口

interface UserRepository extends CrudRepository<User, Long>, CustomizedSave<User> {
}

interface PersonRepository extends CrudRepository<Person, Long>, CustomizedSave<Person> {
}
配置

存储库基础结构尝试通过扫描找到存储库的包下的类来自动检测自定义实现片段。 这些类需要遵循附加默认后缀的命名约定。​​Impl​

以下示例显示了一个使用默认后缀的存储库和一个为后缀设置自定义值的存储库:

例 36。配置示例

爪哇岛

.XML

@EnableLdapRepositories(repositoryImplementationPostfix = "MyPostfix")
class Configuration { … }

前面示例中的第一个配置尝试查找调用充当自定义存储库实现的类。 第二个示例尝试查找。​​com.acme.repository.CustomizedUserRepositoryImpl​​​​com.acme.repository.CustomizedUserRepositoryMyPostfix​

歧义的解决

如果在不同的包中找到具有匹配类名的多个实现,Spring Data 将使用 bean 名称来标识要使用的实现。

给定前面所示的以下两个自定义实现,将使用第一个实现。 它的 Bean 名称是,与片段接口 () 加上后缀的名称相匹配。​​CustomizedUserRepository​​​​customizedUserRepositoryImpl​​​​CustomizedUserRepository​​​​Impl​

例 37。解决不明确的实现

package com.acme.impl.one;

class CustomizedUserRepositoryImpl implements CustomizedUserRepository {

// Your custom implementation
}
package com.acme.impl.two;

@Component("specialCustomImpl")
class CustomizedUserRepositoryImpl implements CustomizedUserRepository {

// Your custom implementation
}

如果您注释接口,则 bean 名称 plusthen 与为存储库实现定义的名称匹配,并且使用它代替第一个。​​UserRepository​​​​@Component("specialCustom")​​​​Impl​​​​com.acme.impl.two​

手动接线

如果您的自定义实现仅使用基于注释的配置和自动连线,则上述方法效果很好,因为它被视为任何其他 Spring Bean。 如果您的实现片段 Bean 需要特殊连接,则可以声明 Bean 并根据​​上一节​​中描述的约定对其进行命名。 然后,基础结构按名称引用手动定义的 Bean 定义,而不是自己创建一个。 以下示例演示如何手动连接自定义实现:

例 38。自定义实现的手动接线

爪哇岛

.XML

class MyClass {
MyClass(@Qualifier("userRepositoryImpl") UserRepository userRepository) {

}
}

5.6.2. 自定义基础仓库

当您想要自定义基本存储库行为以使所有存储库都受到影响时,上​​一节​​中描述的方法需要自定义每个存储库接口。 要改为更改所有存储库的行为,您可以创建一个实现来扩展特定于持久性技术的存储库基类。 然后,此类充当存储库代理的自定义基类,如以下示例所示:

例 39。自定义存储库基类

class MyRepositoryImpl<T, ID>
extends SimpleJpaRepository<T, ID> {

private final EntityManager entityManager;

MyRepositoryImpl(JpaEntityInformation entityInformation,
EntityManager entityManager) {
super(entityInformation, entityManager);

// Keep the EntityManager around to used from the newly introduced methods.
this.entityManager = entityManager;
}

@Transactional
public <S extends T> S save(S entity) {
// implementation goes here
}
}

该类需要具有特定于存储的存储库工厂实现使用的超类的构造函数。 如果存储库基类有多个构造函数,请覆盖采用存储特定基础结构对象(例如模板类)的构造函数。​​EntityInformation​​​​EntityManager​

最后一步是使 Spring 数据基础架构知道自定义的存储库基类。 在配置中,可以使用 来执行此操作,如以下示例所示:​​repositoryBaseClass​

例 40。配置自定义存储库基类

爪哇岛

.XML

@Configuration
@EnableLdapRepositories(repositoryBaseClass = MyRepositoryImpl.class)
class ApplicationConfiguration { … }

5.7. 从聚合根发布事件

存储库管理的实体是聚合根。 在域驱动设计应用程序中,这些聚合根通常发布域事件。 Spring Data 提供了一个注释,称为您可以在聚合根的方法上使用该注释,以使该发布尽可能简单,如以下示例所示:​​@DomainEvents​

例 41。从聚合根公开域事件

class AnAggregateRoot {

@DomainEvents
Collection<Object> domainEvents() {
// … return events you want to get published here
}

@AfterDomainEventPublication
void callbackMethod() {
// … potentially clean up domain events list
}
}

使用的方法可以返回单个事件实例或事件集合。 它绝不能接受任何论据。​​@DomainEvents​

发布所有事件后,我们有一个注释方法。 您可以使用它来潜在地清理要发布的事件列表(以及其他用途)。​​@AfterDomainEventPublication​

每次调用 Spring 数据存储库或方法之一时都会调用这些方法。​​save(…)​​​​saveAll(…)​​​​delete(…)​​​​deleteAll(…)​

5.8. 弹簧数据扩展

本节记录了一组 Spring 数据扩展,这些扩展允许在各种上下文中使用 Spring 数据。 目前,大多数集成都是针对Spring MVC的。

5.8.1. 查询扩展

Querydsl是一个框架,它支持通过其流畅的API构造静态类型的类似SQL的查询。

几个 Spring 数据模块提供与 Querydsl 的集成,如以下示例所示:​​QuerydslPredicateExecutor​

例 42。QuerydslPredicateExecutor interface

public interface QuerydslPredicateExecutor<T> {

Optional<T> findById(Predicate predicate);

Iterable<T> findAll(Predicate predicate);

long count(Predicate predicate);

boolean exists(Predicate predicate);

// … more functionality omitted.
}

查找并返回与 匹配的单个实体。​​Predicate​

查找并返回与 匹配的所有实体。​​Predicate​

返回与 匹配的实体数。​​Predicate​

返回与实体匹配的实体是否存在。​​Predicate​

要使用 Querydsl 支持,请扩展存储库接口,如以下示例所示:​​QuerydslPredicateExecutor​

例 43。存储库上的 querydsl 集成

interface UserRepository extends CrudRepository<User, Long>, QuerydslPredicateExecutor<User> {
}

前面的示例允许您使用 Querydslinstances 编写类型安全的查询,如以下示例所示:​​Predicate​

Predicate predicate = user.firstname.equalsIgnoreCase("dave")
.and(user.lastname.startsWithIgnoreCase("mathews"));

userRepository.findAll(predicate);

5.8.2. 网页支持

支持存储库编程模型的 Spring 数据模块附带了各种 Web 支持。 与Web相关的组件要求Spring MVC JAR位于类路径上。 其中一些甚至提供与Spring HATEOAS的集成。 通常,集成支持是通过在 JavaConfig 配置类中使用注释来启用的,如以下示例所示:​​@EnableSpringDataWebSupport​

例 44。启用 Spring 数据网络支持

爪哇岛

.XML

@Configuration
@EnableWebMvc
@EnableSpringDataWebSupport
class WebConfiguration {}

注释注册了一些组件。 我们将在本节后面讨论这些内容。 它还检测类路径上的Spring HATEOAS,并为其注册集成组件(如果存在)。​​@EnableSpringDataWebSupport​

基本网络支持

在XML中启用Spring Data Web支持

上一节中显示的配置注册了一些基本组件:

  • A使用DomainClassConverter类让Spring MVC 从请求参数或路径变量解析存储库管理的域类的实例。
  • HandlerMethodArgumentResolver实现,让 Spring MVC 从请求参数解析和实例。PageableSort
  • 杰克逊模块,用于反序列化类型,或存储特定的类型,具体取决于所使用的 Spring 数据模块。PointDistance
使用类​​DomainClassConverter​

该类允许您直接在Spring MVC控制器方法签名中使用域类型,这样您就不需要通过存储库手动查找实例,如以下示例所示:​​DomainClassConverter​

例 45。在方法签名中使用域类型的Spring MVC控制器

@Controller
@RequestMapping("/users")
class UserController {

@RequestMapping("/{id}")
String showUserForm(@PathVariable("id") User user, Model model) {

model.addAttribute("user", user);
return "userForm";
}
}

该方法直接接收实例,无需进一步查找。 可以通过让Spring MVC首先将路径变量转换为域类的类型,并最终通过调用为域类型注册的存储库实例来访问实例来解决该实例。​​User​​​​id​​​​findById(…)​

目前,存储库必须实现才有资格被发现进行转换。​​CrudRepository​

用于可分页和排序的处理程序方法参数解析器

​上一节​​中显示的配置片段还注册了 aas 以及 的实例。 注册启用 sandas 有效控制器方法参数,如以下示例所示:​​PageableHandlerMethodArgumentResolver​​​​SortHandlerMethodArgumentResolver​​​​Pageable​​​​Sort​

例 46。使用可分页作为控制器方法参数

@Controller
@RequestMapping("/users")
class UserController {

private final UserRepository repository;

UserController(UserRepository repository) {
this.repository = repository;
}

@RequestMapping
String showUsers(Model model, Pageable pageable) {

model.addAttribute("users", repository.findAll(pageable));
return "users";
}
}

前面的方法签名导致Spring MVC尝试使用以下默认配置从请求参数派生实例:​​Pageable​

表 1.为实例评估的请求参数​​Pageable​

​page​

要检索的页面。0 索引,默认为 0。

​size​

要检索的页面的大小。默认值为 20。

​sort​

应按格式排序的属性。默认排序方向为区分大小写的升序。如果要切换方向或区分大小写,请使用多个参数,例如。​​property,property(,ASC|DESC)(,IgnoreCase)​​​​sort​​​​?sort=firstname&sort=lastname,asc&sort=city,ignorecase​

要定制此行为,请分别注册实现接口或接口的 Bean。 调用 Itsmethod,允许您更改设置,如以下示例所示:​​PageableHandlerMethodArgumentResolverCustomizer​​​​SortHandlerMethodArgumentResolverCustomizer​​​​customize()​

@Bean SortHandlerMethodArgumentResolverCustomizer sortCustomizer() {
return s -> s.setPropertyDelimiter("<-->");
}

如果设置现有属性不足以满足您的目的,请扩展启用 HATEOAS 的等效项,覆盖理论方法,并导入自定义配置文件,而不是使用注释。​​MethodArgumentResolver​​​​SpringDataWebConfiguration​​​​pageableResolver()​​​​sortResolver()​​​​@Enable​

如果您需要从请求中解析多个实例(例如,对于多个表),则可以使用 Spring'sannotation 来区分彼此。 然后,请求参数必须带有前缀。 下面的示例演示生成的方法签名:​​Pageable​​​​Sort​​​​@Qualifier​​​​${qualifier}_​

String showUsers(Model model,
@Qualifier("thing1") Pageable first,
@Qualifier("thing2") Pageable second) { … }

您必须填充,等等。​​thing1_page​​​​thing2_page​

默认传递给方法等效于 a,但您可以通过对参数使用注释来自定义它。​​Pageable​​​​PageRequest.of(0, 20)​​​​@PageableDefault​​​​Pageable​

对可分页的超媒体支持

Spring HATEOAS 附带了一个表示模型类 (),它允许使用必要的元数据和链接来丰富实例的内容,让客户端轻松浏览页面。 ato ais 的转换是通过 Spring HATEOAS接口的实现完成的,称为。 下面的示例演示如何使用 aas 控制器方法参数:​​PagedResources​​​​Page​​​​Page​​​​Page​​​​PagedResources​​​​ResourceAssembler​​​​PagedResourcesAssembler​​​​PagedResourcesAssembler​

例 47。使用 PagedResourcesAssembler 作为控制器方法参数

@Controller
class PersonController {

@Autowired PersonRepository repository;

@RequestMapping(value = "/persons", method = RequestMethod.GET)
HttpEntity<PagedResources<Person>> persons(Pageable pageable,
PagedResourcesAssembler assembler) {

Page<Person> persons = repository.findAll(pageable);
return new ResponseEntity<>(assembler.toResources(persons), HttpStatus.OK);
}
}

启用配置(如前面的示例所示)允许将 thebe 用作控制器方法参数。 呼叫它具有以下效果:​​PagedResourcesAssembler​​​​toResources(…)​

  • 的内容成为实例的内容。PagePagedResources
  • 对象被附加一个实例,并填充来自底层的信息。PagedResourcesPageMetadataPagePageRequest
  • 附上可能获取链接,具体取决于页面的状态。 链接指向方法映射到的 URI。 添加到方法的分页参数与设置匹配,以确保以后可以解析链接。PagedResourcesprevnextPageableHandlerMethodArgumentResolver

假设数据库中有 30 个实例。 您现在可以触发请求 () 并查看类似于以下内容的输出:​​Person​​​​GET http://localhost:8080/persons​

{ "links" : [ { "rel" : "next",
"href" : "http://localhost:8080/persons?page=1&size=20" }
],
"content" : [
… // 20 Person instances rendered here
],
"pageMetadata" : {
"size" : 20,
"totalElements" : 30,
"totalPages" : 2,
"number" : 0
}
}

汇编程序生成了正确的 URI,并且还选取了默认配置,以将参数解析为 afor 即将到来的请求。 这意味着,如果更改该配置,链接将自动遵循更改。 默认情况下,汇编程序指向在其中调用它的控制器方法,但您可以通过传递用作构建分页链接的基础的自定义来自定义该方法,这会重载该方法。​​Pageable​​​​Link​​​​PagedResourcesAssembler.toResource(…)​

弹簧数据杰克逊模块

核心模块,以及一些特定于商店的模块,附带了一组杰克逊模块,用于Spring Data域使用的类似类型。
一旦启用了Web 支持并且可用,这些模块就会被导入。​​org.springframework.data.geo.Distance​​​​org.springframework.data.geo.Point​​​​com.fasterxml.jackson.databind.ObjectMapper​

在初始化期间,就像 一样,由基础结构拾取,以便声明的 Jackson 可用。​​SpringDataJacksonModules​​​​SpringDataJacksonConfiguration​​​​com.fasterxml.jackson.databind.Module​​​​ObjectMapper​

以下域类型的数据绑定混合由通用基础结构注册。

org.springframework.data.geo.Distance
org.springframework.data.geo.Point
org.springframework.data.geo.Box
org.springframework.data.geo.Circle
org.springframework.data.geo.Polygon


单个模块可以提供额外的。
有关更多详细信息,请参阅商店特定部分。​​​SpringDataJacksonModules​


网页数据绑定支持

您可以使用 Spring 数据投影(在投影中描述)通过使用 JSONPath 表达式(需要Jayway JsonPath)或XPath表达式(需要XmlBeam)来绑定传入的请求有效负载,如以下示例所示:

例 48。使用 JSONPath 或 XPath 表达式的 HTTP 有效负载绑定

@ProjectedPayload
public interface UserPayload {

@XBRead("//firstname")
@JsonPath("$..firstname")
String getFirstname();

@XBRead("/lastname")
@JsonPath({ "$.lastname", "$.user.lastname" })
String getLastname();
}

您可以使用前面示例中所示的类型作为 Spring MVC 处理程序方法参数,也可以使用 on 的方法之一。 前面的方法声明将尝试在给定文档中的任何地方查找。 XML 查找在传入文档的顶层执行。 它的 JSON 变体尝试*优先,但如果前者不返回值,也会尝试嵌套在子文档中。 这样,可以轻松缓解源文档结构中的更改,而无需客户端调用公开的方法(通常是基于类的有效负载绑定的缺点)。​​ParameterizedTypeReference​​​​RestTemplate​​​​firstname​​​​lastname​​​​lastname​​​​lastname​​​​user​

支持嵌套投影,如投影中所述。 如果该方法返回复杂的非接口类型,则使用 Jacksonis 映射最终值。​​ObjectMapper​

对于Spring MVC,一旦激活,就会自动注册必要的转换器,并且所需的依赖项在类路径上可用。 如需使用 ,请注册 (JSON) 或手动注册。​​@EnableSpringDataWebSupport​​​​RestTemplate​​​​ProjectingJackson2HttpMessageConverter​​​​XmlBeamHttpMessageConverter​

有关更多信息,请参阅规范的 Spring 数据示例存储库中的Web 投影示例。

查询网络支持

对于那些具有QueryDSL集成的存储,可以从查询字符串中包含的属性派生查询。​​Request​

请考虑以下查询字符串:

?firstname=Dave&lastname=Matthews

给定前面示例中的对象,可以使用 将查询字符串解析为以下值,如下所示:​​User​​​​QuerydslPredicateArgumentResolver​

QUser.user.firstname.eq("Dave").and(QUser.user.lastname.eq("Matthews"))

当在类路径上找到 Querydsl 时,将自动启用该功能。​​@EnableSpringDataWebSupport​

将 ato 添加到方法签名提供了一个即用型,您可以使用 来运行。​​@QuerydslPredicate​​​​Predicate​​​​QuerydslPredicateExecutor​

类型信息通常从方法的返回类型中解析。 由于该信息不一定与域类型匹配,因此最好使用 的属性。​​root​​​​QuerydslPredicate​

下面的示例演示如何在方法中使用签名:​​@QuerydslPredicate​

@Controller
class UserController {

@Autowired UserRepository repository;

@RequestMapping(value = "/", method = RequestMethod.GET)
String index(Model model, @QuerydslPredicate(root = User.class) Predicate predicate,
Pageable pageable, @RequestParam MultiValueMap<String, String> parameters) {

model.addAttribute("users", repository.findAll(predicate, pageable));

return "index";
}
}

将查询字符串参数解析为匹配对象。​​Predicate​​​​User​

默认绑定如下所示:

  • ​Object​​在简单属性上。eq
  • ​Object​​在集合上像属性一样。contains
  • ​Collection​​在简单属性上。in

您可以通过属性 ofor 自定义这些绑定,方法是使用 Java 8 并将方法添加到存储库接口,如下所示:​​bindings​​​​@QuerydslPredicate​​​​default methods​​​​QuerydslBinderCustomizer​

interface UserRepository extends CrudRepository<User, String>,
QuerydslPredicateExecutor<User>,
QuerydslBinderCustomizer<QUser> {

@Override
default void customize(QuerydslBindings bindings, QUser user) {

bindings.bind(user.username).first((path, value) -> path.contains(value))
bindings.bind(String.class)
.first((StringPath path, String value) -> path.containsIgnoreCase(value));
bindings.excluding(user.password);
}
}

​QuerydslPredicateExecutor​​​提供对特定查找器方法的访问。​​Predicate​

​QuerydslBinderCustomizer​​​在存储库界面上定义的自动拾取和快捷方式。​​@QuerydslPredicate(bindings=…)​

将属性的绑定定义为简单绑定。​​username​​​​contains​

将属性的默认绑定定义为不区分大小写的匹配项。​​String​​​​contains​

从解析中排除该属性。​​password​​​​Predicate​

您可以在从存储库或应用特定绑定之前注册持有默认 Querydsl 绑定的 abean。​​QuerydslBinderCustomizerDefaults​​​​@QuerydslPredicate​

5.8.3. 仓库填充器

如果您使用 Spring JDBC 模块,您可能熟悉对填充 SQL 脚本的支持。 类似的抽象在存储库级别可用,尽管它不使用 SQL 作为数据定义语言,因为它必须独立于存储。 因此,填充器支持XML(通过Spring的OXM抽象)和JSON(通过Jackson)来定义用于填充存储库的数据。​​DataSource​

假设您有一个包含以下内容的文件:​​data.json​

例 49。在 JSON 中定义的数据

[ { "_class" : "com.acme.Person",
"firstname" : "Dave",
"lastname" : "Matthews" },
{ "_class" : "com.acme.Person",
"firstname" : "Carter",
"lastname" : "Beauford" } ]

您可以使用 Spring 数据共享中提供的存储库命名空间的填充器元素来填充存储库。 若要将上述数据填充到 ur,请声明类似于以下内容的填充器:​​PersonRepository​

例 50。声明杰克逊存储库填充器

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:repository="http://www.springframework.org/schema/data/repository"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/data/repository
https://www.springframework.org/schema/data/repository/spring-repository.xsd">

<repository:jackson2-populator locations="classpath:data.json" />

</beans>

前面的声明会导致 Jackson 读取和反序列化该文件。​​data.json​​​​ObjectMapper​

JSON 对象解组的类型是通过检查 JSON 文档的属性来确定的。 基础结构最终会选择适当的存储库来处理反序列化的对象。​​_class​

要改用 XML 来定义存储库应填充的数据,您可以使用 theelement。 您可以将其配置为使用 Spring OXM 中可用的 XML 编组选项之一。有关详细信息,请参阅Spring 参考文档。 以下示例显示如何使用 JAXB 取消编组存储库填充器:​​unmarshaller-populator​

例 51。声明解组存储库填充器(使用 JAXB)

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:repository="http://www.springframework.org/schema/data/repository"
xmlns:oxm="http://www.springframework.org/schema/oxm"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/data/repository
https://www.springframework.org/schema/data/repository/spring-repository.xsd
http://www.springframework.org/schema/oxm
https://www.springframework.org/schema/oxm/spring-oxm.xsd">

<repository:unmarshaller-populator locations="classpath:data.json"
unmarshaller-ref="unmarshaller" />

<oxm:jaxb2-marshaller contextPath="com.acme" />

</beans>

Spring Data(数据) LDAP

6. LDAP 存储库

本章指出了 LDAP 存储库支持的特殊性。它建立在使用 Spring 数据存储库中解释的核心存储库支持之上。您应该对那里解释的基本概念有很好的理解。

在使用 Spring LDAP 存储库时,您应该记住以下几点:

  • Spring LDAP 存储库可以通过在 XML 配置中使用 atag 或在配置类上使用 anannotation 来启用。<data-ldap:repositories>@EnableLdapRepositories
  • 要在自动生成的存储库中包含对参数的支持,请扩展您的接口而不是。LdapQueryLdapRepositoryCrudRepository
  • 所有Spring LDAP存储库都必须使用带有ODM注释的实体,如对象目录映射中所述。
  • 由于所有ODM托管类都必须具有可分辨名称作为ID,因此所有Spring LDAP存储库都必须将ID类型参数设置为。 实际上,内置仅采用一个类型参数:托管实体类,该类默认将 ID 设置为。javax.naming.NameLdapRepositoryjavax.naming.Name
  • 由于LDAP协议的特殊性,Spring LDAP存储库不支持分页和排序。

您必须使用 ODM 批注,例如。使用 Spring Data 的注释不起作用,因为 Spring LDAP 使用自己的映射层。​​org.springframework.ldap.odm.annotations.Id​

6.1. 用法

要访问存储在符合 LDAP 标准的目录中的域实体,您可以使用我们完善的存储库支持,从而显著简化实施。为此,请为存储库创建一个接口,如以下示例所示:

例 52。示例人员实体

@Entry(objectClasses = { "person", "top" }, base="ou=someOu")
public class Person {

@Id
private Name dn;

@Attribute(name="cn")
@DnAttribute(value="cn", index=1)
private String fullName;

@Attribute(name="firstName")
private String firstName;

// No @Attribute annotation means this is bound to the LDAP attribute
// with the same value
private String firstName;

@DnAttribute(value="ou", index=0)
@Transient
private String company;

@Transient
private String someUnmappedField;
// ...more attributes below
}

我们这里有一个简单的域对象。请注意,它有一个名为 namedof 类型的属性。使用该域对象,我们可以通过为其定义接口来创建存储库来持久化该类型的对象,如下所示:​​dn​​​​Name​

例 53。持久化的基本存储库接口​​Person​

public interface PersonRepository extends CrudRepository<Person, Long> {

// additional custom finder methods go here
}

目前,此接口仅用于键入目的,但我们稍后可以向其添加其他方法。在 Spring 配置中,添加以下内容:

例 54。通用 LDAP 存储库 Spring 配置

爪哇岛

.XML

@Configuration
@EnableLdapRepositories("com.acme.*.repositories")
class MyConfig {

@Bean
ContextSource contextSource() {

LdapContextSource ldapContextSource = new LdapContextSource();

ldapContextSource.setUserDn("cn=Admin");
ldapContextSource.setPassword("secret");
ldapContextSource.setUrl("ldap://127.0.0.1:389");

return ldapContextSource;
}

@Bean
LdapTemplate ldapTemplate(ContextSource contextSource) {
return new LdapTemplate(contextSource);
}
}

此配置会导致扫描基本软件包以查找扩展接口,并为找到的每个接口创建 Spring bean。 默认情况下,存储库会获得一个被调用的 autowiredSpring bean,因此您只需在偏离此约定时显式配置。​​LdapRepository​​​​LdapTemplate​​​​ldapTemplate​​​​ldap-template-ref​

如果要使用 Java 配置,请使用注释。注释具有与命名空间元素相同的属性。如果未配置基本包,基础结构将扫描带批注的配置类的包。​​@EnableLdapRepositories​

由于我们的域存储库可扩展,因此它为您提供了 CRUD 操作以及访问实体的方法。使用存储库实例是将其注入客户端的依赖项问题。​​CrudRepository​

我们可以向存储库添加分页访问权限,如下所示:

例 55。对个人实体的分页访问

@ExtendWith({SpringExtension.class})
@ContextConfiguration
class PersonRepositoryTests {

@Autowired PersonRepository repository;

@Test
void readAll() {

List<Person> persons = repository.findAll();
assertThat(persons.isEmpty(), is(false));
}
}

该示例使用 Spring 的单元测试支持创建一个应用程序上下文,该上下文将在测试用例中执行基于注释的依赖注入。在测试方法中,我们使用存储库来查询数据存储。

6.2. 查询方法

您通常在存储库上触发的大多数数据访问操作都会导致针对 LDAP 目录运行查询。定义此类查询是在存储库接口上声明方法的问题,如以下示例所示:

例 56。具有查询方法的人员存储库

interface PersonRepository extends PagingAndSortingRepository<Person, String> {

List<Person> findByLastname(String lastname);

List<Person> findByLastnameFirstname(String lastname, String firstname);
}

该方法显示所有给定人员的查询。查询是通过分析可与 and 连接的约束的方法名称派生的。因此,方法名称导致查询表达式。​​lastname​​​​And​​​​Or​​​​(&(objectclass=person)(lastname=lastname))​

该方法显示所有具有给定和的人的查询。查询是通过分析方法名称派生的。因此,方法名称导致查询表达式。​​lastname​​​​firstname​​​​(&(objectclass=person)(lastname=lastname)(firstname=firstname))​

下表提供了可用于查询方法的关键字示例:

表 2.查询方法支持的关键字

关键词

样本

逻辑结果

​LessThanEqual​

​findByAgeLessThanEqual(int age)​

​(attribute⇐age)​

​GreaterThanEqual​

​findByAgeGreaterThanEqual(int age)​

​(attribute>=age)​

​IsNotNull​​​, ​​NotNull​

​findByFirstnameNotNull()​

​(firstname=*)​

​IsNull​​​, ​​Null​

​findByFirstnameNull()​

​(!(firstname=*))​

​Like​

​findByFirstnameLike(String name)​

​(firstname=name)​

​NotLike​​​, ​​IsNotLike​

​findByFirstnameNotLike(String name)​

​(!(firstname=name*))​

​StartingWith​

​findByStartingWith(String name)​

​(firstname=name*)​

​EndingWith​

​findByFirstnameLike(String name)​

​(firstname=*name)​

​Containing​

​findByFirstnameLike(String name)​

​(firstname=*name*)​

​(No keyword)​

​findByFirstname(String name)​

​(Firstname=name)​

​Not​

​findByFirstnameNot(String name)​

​(!(Firstname=name))​

6.2.1. 预测

Spring 数据查询方法通常返回由存储库管理的聚合根的一个或多个实例。 但是,有时可能需要基于这些类型的某些属性创建投影。 Spring 数据允许对专用返回类型进行建模,以更有选择性地检索托管聚合的部分视图。

假设存储库和聚合根类型,如以下示例所示:

例 57。示例聚合和存储库

class Person {

@Id UUID id;
String firstname, lastname;
Address address;

static class Address {
String zipCode, city, street;
}
}

interface PersonRepository extends Repository<Person, UUID> {

Collection<Person> findByLastname(String lastname);
}

现在假设我们只想检索人员的姓名属性。 Spring Data提供了什么手段来实现这一目标?本章的其余部分将回答这个问题。

基于接口的投影

将查询结果限制为仅 name 属性的最简单方法是声明一个接口,该接口公开要读取的属性的访问器方法,如以下示例所示:

例 58。用于检索属性子集的投影接口

interface NamesOnly {

String getFirstname();
String getLastname();
}

这里重要的一点是,此处定义的属性与聚合根中的属性完全匹配。 这样做可以添加查询方法,如下所示:

例 59。使用基于接口的投影和查询方法的存储库

interface PersonRepository extends Repository<Person, UUID> {

Collection<NamesOnly> findByLastname(String lastname);
}

查询执行引擎在运行时为返回的每个元素创建该接口的代理实例,并将对公开方法的调用转发到目标对象。

在 yourThat 中声明方法会覆盖基方法(例如,声明在、特定于存储的存储库接口或 the)会导致对基方法的调用,而不管声明的返回类型如何。请确保使用兼容的返回类型,因为基方法不能用于投影。某些存储模块支持注释,以将重写的基方法转换为查询方法,然后可用于返回投影。​​Repository​​​​CrudRepository​​​​Simple…Repository​​​​@Query​

投影可以递归使用。如果还希望包含某些信息,请为其创建一个投影接口,并从声明中返回该接口,如以下示例所示:​​Address​​​​getAddress()​

例 60。用于检索属性子集的投影接口

interface PersonSummary {

String getFirstname();
String getLastname();
AddressSummary getAddress();

interface AddressSummary {
String getCity();
}
}

在方法调用时,将获取目标实例的属性并依次包装到投影代理中。​​address​

封闭式投影

其访问器方法都与目标聚合的属性匹配的投影接口被视为封闭投影。以下示例(我们在本章前面也使用过)是一个封闭投影:

例 61。封闭投影

interface NamesOnly {

String getFirstname();
String getLastname();
}

如果您使用封闭投影,Spring Data 可以优化查询执行,因为我们知道支持投影代理所需的所有属性。 有关这方面的更多详细信息,请参阅参考文档中特定于模块的部分。

开放投影

投影接口中的访问器方法还可用于通过注释来计算新值,如以下示例所示:​​@Value​

例 62。开放式投影

interface NamesOnly {

@Value("#{target.firstname + ' ' + target.lastname}")
String getFullName();

}

支持投影的聚合根在变量中可用。 投影接口使用是开放式投影。 在这种情况下,Spring 数据无法应用查询执行优化,因为 SpEL 表达式可以使用聚合根的任何属性。​​target​​​​@Value​

使用的表达式不应该太复杂 — 您希望避免对变量进行编程。 对于非常简单的表达式,一种选择可能是采用默认方法(Java 8 中引入),如以下示例所示:​​@Value​​​​String​

例 63。使用自定义逻辑的默认方法的投影接口

interface NamesOnly {

String getFirstname();
String getLastname();

default String getFullName() {
return getFirstname().concat(" ").concat(getLastname());
}
}

此方法要求您能够完全基于投影接口上公开的其他访问器方法实现逻辑。 第二个更灵活的选项是在 Spring Bean 中实现自定义逻辑,然后从 SpEL 表达式中调用该逻辑,如以下示例所示:

例 64。示例人员对象

@Component
class MyBean {

String getFullName(Person person) {

}
}

interface NamesOnly {

@Value("#{@myBean.getFullName(target)}")
String getFullName();

}

请注意 SpEL 表达式如何引用和调用该方法,并将投影目标作为方法参数转发。 SpEL 表达式评估支持的方法也可以使用方法参数,然后可以从表达式中引用这些参数。 方法参数可通过名为的数组获得。下面的示例演示如何从数组中获取方法参数:​​myBean​​​​getFullName(…)​​​​Object​​​​args​​​​args​

例 65。示例人员对象

interface NamesOnly {

@Value("#{args[0] + ' ' + target.firstname + '!'}")
String getSalutation(String prefix);
}

同样,对于更复杂的表达式,您应该使用 Spring Bean 并让表达式调用方法,如前所述。

可为空的包装器

投影接口中的 Getter 可以使用可为空的包装器来提高空安全性。目前支持的包装器类型包括:

  • ​java.util.Optional​
  • ​com.google.common.base.Optional​
  • ​scala.Option​
  • ​io.vavr.control.Option​

例 66。使用可为空包装器的投影接口

interface NamesOnly {

Optional<String> getFirstname();
}

如果基础投影值不是,则使用包装器的当前表示形式返回值。 如果支持值为 ,则 getter 方法返回所用包装类型的空表示形式。​​null​​​​null​

基于类的投影 (DTO)

定义投影的另一种方法是使用值类型 DTO(数据传输对象),用于保存应检索的字段的属性。 这些 DTO 类型的使用方式与投影接口的使用方式完全相同,只是不会发生代理,并且不能应用嵌套投影。

如果存储通过限制要加载的字段来优化查询执行,则要加载的字段由公开的构造函数的参数名称确定。

以下示例显示了一个投影 DTO:

例 67。一个突出的DTO

class NamesOnly {

private final String firstname, lastname;

NamesOnly(String firstname, String lastname) {

this.firstname = firstname;
this.lastname = lastname;
}

String getFirstname() {
return this.firstname;
}

String getLastname() {
return this.lastname;
}

// equals(…) and hashCode() implementations
}

避免投影 DTO 的样板代码


你可以通过使用ProjectLombok​来大大简化DTO的代码,它提供了anannotation(不要与前面的接口示例中所示的Spring'sannotation混淆)。 如果您使用龙目岛项目的注释,前面显示的示例 DTO 将变为以下内容:​​@Value​​​​@Value​​​​@Value​




@Value
class NamesOnly {
String firstname, lastname;
}




字段是默认的,该类公开一个构造函数,该构造函数采用所有字段并自动获取实现的 sand方法。​​private final​​​​equals(…)​​​​hashCode()​


动态投影

到目前为止,我们已经使用投影类型作为集合的返回类型或元素类型。 但是,您可能希望选择要在调用时使用的类型(这使其成为动态的)。 若要应用动态投影,请使用查询方法,如以下示例所示:

例 68。使用动态投影参数的存储库

interface PersonRepository extends Repository<Person, UUID> {

<T> Collection<T> findByLastname(String lastname, Class<T> type);
}

这样,该方法可用于按原样获取聚合或应用投影,如以下示例所示:

例 69。使用具有动态投影的存储库

void someMethod(PersonRepository people) {

Collection<Person> aggregates =
people.findByLastname("Matthews", Person.class);

Collection<NamesOnly> aggregates =
people.findByLastname("Matthews", NamesOnly.class);
}

检查类型的查询参数是否符合动态投影参数的条件。 如果查询的实际返回类型等于参数的泛型参数类型,则匹配参数不可用于查询或 SpEL 表达式。 如果要使用 aparameter 作为查询参数,请确保使用其他泛型参数,例如。​​Class​​​​Class​​​​Class​​​​Class​​​​Class<?>​

6.2.2. 查询DSL支持

基本查询DSL支持包含在Spring LDAP中。此支持包括以下内容:

  • 一个注释处理器,用于基于Spring LDAP ODM注释生成QueryDSL类。有关 ODM 注释的详细信息,请参阅对象目录映射。LdapAnnotationProcessor
  • 一个查询实现,用于在代码中生成和运行 QueryDSL 查询。QueryDslLdapQuery
  • Spring 数据存储库支持 QueryDSL 谓词,包括许多具有适当参数的附加方法。您可以扩展此接口(以及)以将此支持包含在存储库中。QueryDslPredicateExecutorLdapRepository

6.3. 其他

6.3.1. CDI 集成

存储库接口的实例通常由容器创建,在使用 Spring 数据时,Spring 是最自然的选择。从版本2.1开始,Spring Data LDAP包含一个自定义CDI扩展,允许您在CDI环境中使用存储库抽象。扩展是 JAR 的一部分。要激活它,请将 Spring Data LDAP JAR 放入您的类路径中。现在,您可以通过实现 CDI 创建器来设置基础结构,如以下示例所示:​​LdapTemplate​

class LdapTemplateProducer {

@Produces
@ApplicationScoped
public LdapOperations createLdapTemplate() {

ContextSource contextSource = …
return new LdapTemplate(contextSource);
}
}

Spring Data LDAP CDI 扩展选取 CDI Bean,并在容器请求存储库类型的 Bean 时为 Spring Data 存储库创建代理。因此,获取 Spring 数据存储库的实例是声明注入属性的问题,如以下示例所示:​​LdapTemplate​

class RepositoryClient {

@Inject
PersonRepository repository;

public void businessMethod() {
List<Person> people = repository.findAll();
}
}

Spring Data(数据) LDAP

附录

附录 A:命名空间引用

元素​​<repositories />​

该元素触发了 Spring 数据存储库基础结构的设置。最重要的属性是,它定义了要扫描 Spring 数据存储库接口的包。请参阅“XML 配置”。下表描述了元素的属性:​​<repositories />​​​​base-package​​​​<repositories />​

表 3.属性

名字

描述

​base-package​

定义要扫描的包,以查找在自动检测模式下扩展的存储库接口(实际接口由特定的 Spring 数据模块确定)。还会扫描已配置软件包下的所有软件包。允许使用通配符。​​*Repository​

​repository-impl-postfix​

定义用于自动检测自定义存储库实现的后缀。名称以配置的后缀结尾的类被视为候选类。默认为。​​Impl​

​query-lookup-strategy​

确定用于创建查找器查询的策略。有关详细信息,请参阅“查询查找策略​”。默认为。​​create-if-not-found​

​named-queries-location​

定义搜索包含外部定义查询的属性文件的位置。

​consider-nested-repositories​

是否应考虑嵌套存储库接口定义。默认为。​​false​

附录 B:填充器命名空间参考

<填充器 /> 元素

该元素允许通过 Spring 数据存储库基础架构填充数据存储。[​​1​]​<populator />​

表 4.属性

名字

描述

​locations​

应填充从存储库中查找要读取对象的文件的位置。

附录 C:存储库查询关键字

支持的查询方法主题关键字

下表列出了 Spring 数据存储库查询派生机制通常支持的主题关键字,以表达谓词。 有关支持的关键字的确切列表,请参阅特定于商店的文档,因为此处列出的某些关键字可能在特定商店中不受支持。

表 5.查询主题关键字

关键词

描述

​find…By​​​, , , , , ​​read…By​​​​get…By​​​​query…By​​​​search…By​​​​stream…By​

常规查询方法通常返回存储库类型、主子类型或结果包装器(如),或任何其他特定于存储的结果包装器。可以用作,或与其他关键字结合使用。​​Collection​​​​Streamable​​​​Page​​​​GeoResults​​​​findBy…​​​​findMyDomainTypeBy…​

​exists…By​

存在投影,返回通常为结果。​​boolean​

​count…By​

计数投影返回数值结果。

​delete…By​​​, ​​remove…By​

删除查询方法不返回任何结果 () 或删除计数。​​void​

​…First<number>…​​​, ​​…Top<number>…​

将查询结果限制为第一个结果。此关键字可以出现在主题(和其他关键字)之间的任何位置。​​<number>​​​​find​​​​by​

​…Distinct…​

使用非重复查询仅返回唯一结果。请参阅特定于商店的文档是否支持该功能。此关键字可以出现在主题(和其他关键字)之间的任何位置。​​find​​​​by​

支持的查询方法谓词关键字和修饰符

下表列出了 Spring 数据存储库查询派生机制通常支持的谓词关键字。 但是,请参阅特定于商店的文档,了解支持的关键字的确切列表,因为此处列出的某些关键字可能在特定商店中不受支持。

表 6.查询谓词关键字

逻辑关键字

关键字表达式

​AND​

​And​

​OR​

​Or​

​AFTER​

​After​​​, ​​IsAfter​

​BEFORE​

​Before​​​, ​​IsBefore​

​CONTAINING​

​Containing​​​, , ​​IsContaining​​​​Contains​

​BETWEEN​

​Between​​​, ​​IsBetween​

​ENDING_WITH​

​EndingWith​​​, , ​​IsEndingWith​​​​EndsWith​

​EXISTS​

​Exists​

​FALSE​

​False​​​, ​​IsFalse​

​GREATER_THAN​

​GreaterThan​​​, ​​IsGreaterThan​

​GREATER_THAN_EQUALS​

​GreaterThanEqual​​​, ​​IsGreaterThanEqual​

​IN​

​In​​​, ​​IsIn​

​IS​

​Is​​​,,(或无关键字)​​Equals​

​IS_EMPTY​

​IsEmpty​​​, ​​Empty​

​IS_NOT_EMPTY​

​IsNotEmpty​​​, ​​NotEmpty​

​IS_NOT_NULL​

​NotNull​​​, ​​IsNotNull​

​IS_NULL​

​Null​​​, ​​IsNull​

​LESS_THAN​

​LessThan​​​, ​​IsLessThan​

​LESS_THAN_EQUAL​

​LessThanEqual​​​, ​​IsLessThanEqual​

​LIKE​

​Like​​​, ​​IsLike​

​NEAR​

​Near​​​, ​​IsNear​

​NOT​

​Not​​​, ​​IsNot​

​NOT_IN​

​NotIn​​​, ​​IsNotIn​

​NOT_LIKE​

​NotLike​​​, ​​IsNotLike​

​REGEX​

​Regex​​​, , ​​MatchesRegex​​​​Matches​

​STARTING_WITH​

​StartingWith​​​, , ​​IsStartingWith​​​​StartsWith​

​TRUE​

​True​​​, ​​IsTrue​

​WITHIN​

​Within​​​, ​​IsWithin​

除了筛选器谓词之外,还支持以下修饰符列表:

表 7.查询谓词修饰符关键字

关键词

描述

​IgnoreCase​​​, ​​IgnoringCase​

与谓词关键字一起使用,用于不区分大小写的比较。

​AllIgnoreCase​​​, ​​AllIgnoringCase​

忽略所有合适属性的大小写。在查询方法谓词中的某处使用。

​OrderBy…​

指定静态排序顺序,后跟属性路径和方向(例如)。​​OrderByFirstnameAscLastnameDesc​

附录 D:存储库查询返回类型

支持的查询返回类型

下表列出了 Spring 数据存储库通常支持的返回类型。 但是,请参阅特定于商店的文档以获取支持的返回类型的确切列表,因为此处列出的某些类型可能在特定商店中不受支持。

地理空间类型(如、和)仅适用于支持地理空间查询的数据存储。 某些存储模块可能会定义自己的结果包装器类型。​​GeoResult​​​​GeoResults​​​​GeoPage​

表 8.查询返回类型

返回类型

描述

​void​

表示无返回值。

Java 原语。

包装器类型

Java 包装器类型。

​T​

一个独特的实体。期望查询方法最多返回一个结果。如果未找到结果,则返回。多个结果触发 an.​​null​​​​IncorrectResultSizeDataAccessException​

​Iterator<T>​

一。​​Iterator​

​Collection<T>​

一个。​​Collection​

​List<T>​

一个。​​List​

​Optional<T>​

爪哇8或番石榴。期望查询方法最多返回一个结果。如果未找到结果,则返回 oris 。多个结果触发 an.​​Optional​​​​Optional.empty()​​​​Optional.absent()​​​​IncorrectResultSizeDataAccessException​

​Option<T>​

要么是斯卡拉,要么是vavrtype。在语义上与前面描述的Java 8的行为相同。​​Option​​​​Optional​

​Stream<T>​

A Java 8 .​​Stream​

​Streamable<T>​

该直接的便利扩展公开了流式传输,映射和过滤结果,连接它们等的方法。​​Iterable​

实现和采用构造函数或工厂方法参数的类型​​Streamable​​​​Streamable​

公开采用 aas 参数的构造函数或/工厂方法的类型。有关详细信息,请参阅返回自定义可流式传输包装器类型​。​​….of(…)​​​​….valueOf(…)​​​​Streamable​

瓦夫尔,,,​​Seq​​​​List​​​​Map​​​​Set​

Vavr 集合类型。有关详细信息,请参阅​​对 Vavr 集合的支持​​。

​Future<T>​

A. 期望对方法进行注释,并且需要启用 Spring 的异步方法执行功能。​​Future​​​​@Async​

​CompletableFuture<T>​

A Java 8.期望对方法进行注释,并且需要启用 Spring 的异步方法执行功能。​​CompletableFuture​​​​@Async​

​Slice<T>​

一个大小的数据块,指示是否有更多可用数据。需要方法参数。​​Pageable​

​Page<T>​

A 包含其他信息,例如结果总数。需要方法参数。​​Slice​​​​Pageable​

​GeoResult<T>​

包含附加信息(如到参考位置的距离)的结果条目。

​GeoResults<T>​

包含附加信息的列表,例如到参考位置的平均距离。​​GeoResult<T>​

​GeoPage<T>​

Awith,例如到参考位置的平均距离。​​Page​​​​GeoResult<T>​

​Mono<T>​

使用反应式存储库发射零个或一个元素的项目反应器。期望查询方法最多返回一个结果。如果未找到结果,则返回。多个结果触发 an.​​Mono​​​​Mono.empty()​​​​IncorrectResultSizeDataAccessException​

​Flux<T>​

使用反应式存储库发射零个、一个或多个元素的项目反应器。返回的查询还可以发出无限数量的元素。​​Flux​​​​Flux​

​Single<T>​

一个 RxJava使用反应式存储库发出单个元素。期望查询方法最多返回一个结果。如果未找到结果,则返回。多个结果触发 an.​​Single​​​​Mono.empty()​​​​IncorrectResultSizeDataAccessException​

​Maybe<T>​

使用反应式存储库的 RxJavaemitting 零个或一个元素。期望查询方法最多返回一个结果。如果未找到结果,则返回。多个结果触发 an.​​Maybe​​​​Mono.empty()​​​​IncorrectResultSizeDataAccessException​

​Flowable<T>​

使用反应式存储库的 RxJavaemitting 零个、一个或多个元素。返回的查询还可以发出无限数量的元素。​​Flowable​​​​Flowable​