Spring Data (数据)MongoDB

时间:2022-11-21 18:12:03

版本 4.0.0

Spring Data MongoDB项目将Spring的核心概念应用于使用MongoDB文档样式数据存储的解决方案的开发。我们提供了一个“模板”作为存储和查询文档的高级抽象。您可能会注意到与 Spring 框架提供的 JDBC 支持的相似之处。

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

本节提供了一些对 Spring 和文档数据库的基本介绍。本文档的其余部分仅涉及Spring Data MongoDB功能,并假设用户熟悉MongoDB和Spring概念。

Spring Data (数据)MongoDB

1. 学习之春

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

  • 国际奥委会容器
  • 型式转换系统
  • 表达式语言
  • JMX 集成
  • DAO 异常层次结构。

虽然你不需要知道Spring API,但理解它们背后的概念是很重要的。至少,控制反转 (IoC) 背后的想法应该很熟悉,并且您应该熟悉您选择使用的任何 IoC 容器。

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

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

2. 学习NoSQL和文档数据库

NoSQL存储已经席卷了存储世界。这是一个广阔的领域,拥有大量的解决方案、术语和模式(更糟糕的是,甚至术语本身也有多重含义)。虽然有些原则很常见,但您必须在某种程度上熟悉MongoDB。熟悉的最佳方法是阅读文档并遵循示例。完成它们通常不会超过 5-10 分钟,特别是如果您来自仅 RDMBS 的背景,这些练习可能会让您大开眼界。

了解MongoDB的起点是www.mongodb.org。以下是其他有用资源的列表:

  • 该手册介绍了MongoDB,并包含入门指南,参考文档和教程的链接。
  • 在线 shell结合在线教程提供了一种与 MongoDB 实例交互的便捷方式。
  • MongoDB Java Language Center.
  • 您可以购买几本书。
  • Karl Seguin的在线书籍:The Little MongoDB Book。

3. 要求

Spring Data MongoDB 4.x二进制文件需要JDK 17及以上版本和Spring Framework6.0.0及更高版本。

在文档存储方面,您至少需要MongoDB 的 3.6 版本,尽管我们建议使用更新的版本。

3.1. 兼容性矩阵

以下兼容性矩阵总结了 Spring 数据版本到 MongoDB 驱动程序/数据库版本。 数据库版本显示通过 Spring 数据测试套件的最高支持服务器版本。 您可以使用较新的服务器版本,除非您的应用程序使用受MongoDB 服务器更改影响的功能。 另请参阅官方MongoDB驱动程序兼容性矩阵,了解驱动程序和服务器版本兼容性。

春季数据发布列车

春季数据 MongoDB

驱动程序版本

服务器版本

2022.0

​4.0.x​

​4.7.x​

​6.0.x​

2021.2

​3.4.x​

​4.6.x​

​5.0.x​

2021.1

​3.3.x​

​4.4.x​

​5.0.x​

2021.0

​3.2.x​

​4.1.x​

​4.4.x​

2020.0

​3.1.x​

​4.1.x​

​4.4.x​

纽曼

​3.0.x​

​4.0.x​

​4.4.x​

摩尔

​2.2.x​

​3.11.x/Reactive Streams 1.12.x​

​4.2.x​

洛夫莱斯

​2.1.x​

​3.8.x/Reactive Streams 1.9.x​

​4.0.x​

3.1.1. MongoDB 4.4 中的相关变化

  • 当不存在条件时,字段列表不得包含文本搜索分数属性。另请参阅$text运算符$text
  • 运行 map Reduce 时,排序不得为空文档。

3.1.2. MongoDB 4.2 中的相关变化

  • 删除命令。另请参阅删除地理邻近geoNear
  • 删除命令。另请参阅删除评估eval

4. 其他帮助资源

学习一个新框架并不总是那么简单。 在本节中,我们尝试提供我们认为易于遵循的指南,以便从Spring Data MongoDB模块开始。 但是,如果您遇到问题或需要建议,请随时使用以下链接之一:

社区论坛

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

专业支持

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

5. 后续开发

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

6. 升级

6.1. 升级弹簧数据

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

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

6.2. 升级 MongoDB 驱动程序

Spring Data MongoDB 4.x需要MongoDB Java Driver 4.8.x
要了解有关驱动程序版本的更多信息,请访问MongoDB文档。

7. 依赖关系

由于各个 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>

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

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

7.2. 弹簧框架

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

8. 使用 Spring 数据存储库

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


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



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


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

8.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
@EnableMongoRepositories
class Config { … }

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

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

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


class SomeClient {

private final PersonRepository repository;

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

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

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

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

8.3. 定义存储库接口

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

8.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​

8.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 { … }

8.4. 定义查询方法

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

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

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

8.4.1. 查询查找策略

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

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

8.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

8.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 命名约定(即,不要在属性名称中使用下划线,而是使用驼峰大小写)。

8.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()));

8.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​

8.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 采集类型

使用的 Vavr 实现类型

有效的 Java 源类型

​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​

8.4.7. 存储库方法的空处理

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

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

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

可为空性注释

您可以使用 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​

8.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>​

8.4.9. 异步查询结果

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

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

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

Use as the return type.​​java.util.concurrent.Future​

Use a Java 8 as the return type.​​java.util.concurrent.CompletableFuture​

8.5. Creating Repository Instances

This section covers how to create instances and bean definitions for the defined repository interfaces.

8.5.1. Java 配置

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

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

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

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

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

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

8.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​

8.5.3. 使用过滤器

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

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

例 27。使用过滤器

爪哇岛

.XML

@Configuration
@EnableMongoRepositories(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​

8.5.4. 独立使用

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

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

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

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

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

8.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

@EnableMongoRepositories(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) {

}
}

8.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
@EnableMongoRepositories(repositoryBaseClass = MyRepositoryImpl.class)
class ApplicationConfiguration { … }

8.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(…)​

8.8. 弹簧数据扩展

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

8.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);

8.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​

During initialization , like the , get picked up by the infrastructure, so that the declared s are made available to the Jackson .​​SpringDataJacksonModules​​​​SpringDataJacksonConfiguration​​​​com.fasterxml.jackson.databind.Module​​​​ObjectMapper​

Data binding mixins for the following domain types are registered by the common infrastructure.

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


The individual module may provide additional .
Please refer to the store specific section for more details.​​​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​

8.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 (数据)MongoDB

9. 简介

9.1. 文档结构

参考文档的这一部分解释了Spring Data MongoDB提供的核心功能。

“MongoDB支持”引入了MongoDB模块功能集。

“MongoDB存储库”介绍了对MongoDB的存储库支持。

10. 蒙戈数据库支持

MongoDB支持包含广泛的功能:

  • Spring 配置支持基于 Java 的类或 Mongo 驱动程序实例和副本集的 XML 命名空间。@Configuration
  • ​MongoTemplate​​帮助程序类,用于在执行常见 Mongo 操作时提高工作效率。包括文档和 POJO 之间的集成对象映射。
  • 异常转换为 Spring 的可移植数据访问异常层次结构。
  • 功能丰富的对象映射与 Spring 的转换服务集成。
  • 可扩展以支持其他元数据格式的基于批注的映射元数据。
  • 持久性和映射生命周期事件。
  • 基于 Java 的查询、条件和更新 DSL。
  • 自动实现存储库接口,包括对自定义查找器方法的支持。
  • 查询DSL集成以支持类型安全查询。
  • 对 JPA 实体的跨存储持久性支持,其中字段使用 MongoDB 透明地持久化和检索(已弃用 - 无需替换即可删除)。
  • 地理空间集成。

对于大多数任务,您应该使用存储库支持,它既利用丰富的映射 functionality.is 查找访问功能(如递增计数器或临时 CRUD 操作)的位置。还提供了回调方法,以便您轻松获取低级 API 工件,例如直接与 MongoDB 通信。各种API工件上的命名约定的目标是复制基本MongoDB Java驱动程序中的这些约定,以便您可以轻松地将现有知识映射到Spring API上。​​MongoTemplate​​​​MongoTemplate​​​​MongoTemplate​​​​com.mongodb.client.MongoDatabase​

10.1. 入门

引导设置工作环境的一种简单方法是在STS 中创建一个基于 Spring 的项目。

首先,您需要设置一个正在运行的MongoDB服务器。有关如何启动MongoDB实例的说明,请参阅MongoDB快速入门指南。安装后,启动MongoDB通常是运行以下命令的问题:​​${MONGO_HOME}/bin/mongod​

要在 STS 中创建 Spring 项目,请执行以下操作:

  1. 转到→“新建→ Spring 模板项目”→“简单 Spring 实用程序项目”,并在出现提示时按“是”。然后输入项目和包名称,例如。org.spring.mongodb.example
  2. 将以下内容添加到 pom.xml 文件元素:dependencies
<dependencies>

<!-- other dependency elements omitted -->

<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb</artifactId>
<version>4.0.0</version>
</dependency>

</dependencies>
  1. 将 pom 中的 Spring 版本.xml更改为
<spring.framework.version>6.0.0</spring.framework.version>
  1. 将 Maven 的 Spring 里程碑存储库的以下位置添加到您的位置,使其与 yourelement 处于同一级别:pom.xml<dependencies/>
<repositories>
<repository>
<id>spring-milestone</id>
<name>Spring Maven MILESTONE Repository</name>
<url>https://repo.spring.io/libs-milestone</url>
</repository>
</repositories>

存储库也可以在此处浏览。

您可能还希望将日志记录级别设置为 以查看一些其他信息。为此,请编辑文件以包含以下内容:​​DEBUG​​​​log4j.properties​

log4j.category.org.springframework.data.mongodb=DEBUG
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %40.40c:%4L - %m%n

然后,您可以创建 aclass 来持久化:​​Person​

package org.spring.mongodb.example;

public class Person {

private String id;
private String name;
private int age;

public Person(String name, int age) {
this.name = name;
this.age = age;
}

public String getId() {
return id;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}

@Override
public String toString() {
return "Person [id=" + id + ", name=" + name + ", age=" + age + "]";
}
}

您还需要一个主应用程序才能运行:

package org.spring.mongodb.example;

public class MongoApp {

private static final Log log = LogFactory.getLog(MongoApp.class);

public static void main(String[] args) throws Exception {

MongoOperations mongoOps = new MongoTemplate(MongoClients.create(), "database");
mongoOps.insert(new Person("Joe", 34));

log.info(mongoOps.findOne(new Query(where("name").is("Joe")), Person.class));

mongoOps.dropCollection("person");
}
}

运行主程序时,前面的示例将生成以下输出:

10:01:32,062 DEBUG apping.MongoPersistentEntityIndexCreator:  80 - Analyzing class class org.spring.example.Person for index information.
10:01:32,265 DEBUG ramework.data.mongodb.core.MongoTemplate: 631 - insert Document containing fields: [_class, age, name] in collection: Person
10:01:32,765 DEBUG ramework.data.mongodb.core.MongoTemplate:1243 - findOne using query: { "name" : "Joe"} in db.collection: database.Person
10:01:32,953 INFO org.spring.mongodb.example.MongoApp: 25 - Person [id=4ddbba3c0be56b7e1b210166, name=Joe, age=34]
10:01:32,984 DEBUG ramework.data.mongodb.core.MongoTemplate: 375 - Dropped collection [database.person]

即使在这个简单的示例中,也很少有需要注意的事项:

  • 你可以实例化Spring Mongo,MongoTemplate的中心助手类,通过使用标准对象和要使用的数据库的名称。com.mongodb.client.MongoClient
  • 映射器针对标准 POJO 对象工作,而无需任何其他元数据(尽管您可以选择提供该信息。看这里。
  • 约定用于处理字段,将其转换为存储在数据库中时。idObjectId
  • 映射约定可以使用字段访问。请注意,该类只有 getter。Person
  • 如果构造函数参数名称与存储文档的字段名称匹配,则它们用于实例化对象

10.2. 示例存储库

有一个GitHub 存储库,其中包含几个示例,您可以下载并试用这些示例,以了解库的工作原理。

10.3. 使用 Spring 连接到 MongoDB

使用MongoDB和Spring时的首要任务之一是使用IoC容器创建一个对象。有两种主要方法可以做到这一点,一种是使用基于 Java 的 Bean 元数据,另一种是使用基于 XML 的 Bean 元数据。以下各节将讨论这两种情况。​​com.mongodb.client.MongoClient​

对于那些不熟悉如何使用基于 Java 的 Bean 元数据而不是基于 XML 的元数据来配置 Spring 容器的人,请参阅此处参考文档中的高级介绍以及此处的详细文档。

10.3.1. 使用基于 Java 的元数据注册 Mongo 实例

以下示例显示了使用基于 Java 的 Bean 元数据注册 的实例的示例:​​com.mongodb.client.MongoClient​

例 52。使用基于 Java 的 Bean 元数据注册对象​​com.mongodb.client.MongoClient​

@Configuration
public class AppConfig {

/*
* Use the standard Mongo driver API to create a com.mongodb.client.MongoClient instance.
*/
public @Bean MongoClient mongoClient() {
return MongoClients.create("mongodb://localhost:27017");
}
}

这种方法允许您使用标准实例,容器使用 Spring 的。与直接实例化实例相比,它还具有为容器提供实现的额外优势,该实现将MongoDB异常转换为Spring的可移植层次结构中的异常,用于使用注释注释的数据访问类。这种层次结构和用法在Spring 的 DAO 支持功能中进行了描述。​​com.mongodb.client.MongoClient​​​​MongoClientFactoryBean​​​​com.mongodb.client.MongoClient​​​​FactoryBean​​​​ExceptionTranslator​​​​DataAccessException​​​​@Repository​​​​@Repository​

以下示例显示了一个基于 Java 的 Bean 元数据的示例,该元数据支持对带注释的类进行异常转换:​​@Repository​

例 53。使用 Spring 的沙子注册对象,启用 Spring 的异常转换支持​​com.mongodb.client.MongoClient​​​​MongoClientFactoryBean​

@Configuration
public class AppConfig {

/*
* Factory bean that creates the com.mongodb.client.MongoClient instance
*/
public @Bean MongoClientFactoryBean mongo() {
MongoClientFactoryBean mongo = new MongoClientFactoryBean();
mongo.setHost("localhost");
return mongo;
}
}

要访问由其他类或您自己的类创建的对象,请使用 afield。​​com.mongodb.client.MongoClient​​​​MongoClientFactoryBean​​​​@Configuration​​​​private @Autowired MongoClient mongoClient;​

10.3.2. 使用基于 XML 的元数据注册 Mongo 实例

虽然您可以使用 Spring 的传统 XML 命名空间来注册容器的实例,但 XML 可能非常冗长,因为它是通用的。XML 命名空间是配置常用对象(如 Mongo 实例)的更好替代方法。mongo 命名空间允许您创建 mongo 实例服务器位置、副本集和选项。​​<beans/>​​​​com.mongodb.client.MongoClient​

要使用 Mongo 命名空间元素,您需要引用 Mongo 架构,如下所示:

例 54。用于配置 MongoDB 的 XML 模式

<?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:mongo="http://www.springframework.org/schema/data/mongo"
xsi:schemaLocation=
"
http://www.springframework.org/schema/data/mongo https://www.springframework.org/schema/data/mongo/spring-mongo.xsd
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">

<!-- Default bean name is 'mongo' -->
<mongo:mongo-client host="localhost" port="27017"/>

</beans>

以下示例显示了更高级的配置(请注意,这些不是推荐的值):​​MongoClientSettings​

例 55。用于配置对象的 XML 架构​​com.mongodb.client.MongoClient​​​​MongoClientSettings​

<beans>

<mongo:mongo-client host="localhost" port="27017">
<mongo:client-settings connection-pool-max-connection-life-time="10"
connection-pool-min-size="10"
connection-pool-max-size="20"
connection-pool-maintenance-frequency="10"
connection-pool-maintenance-initial-delay="11"
connection-pool-max-connection-idle-time="30"
connection-pool-max-wait-time="15" />
</mongo:mongo-client>

</beans>

以下示例显示了使用副本集的配置:

例 56。使用副本集配置对象的 XML 架构​​com.mongodb.client.MongoClient​

<mongo:mongo-client  replica-set="rs0">
<mongo:client-settings cluster-hosts="127.0.0.1:27017,localhost:27018" />
</mongo:mongo-client>

10.3.3. Mongo数据库工厂接口

虽然是MongoDB驱动程序API的入口点,但连接到特定的MongoDB数据库实例需要其他信息,例如数据库名称以及可选的用户名和密码。使用该信息,您可以获取对象并访问特定MongoDB数据库实例的所有功能。Spring 提供了接口(如以下列表所示)来引导与数据库的连接:​​com.mongodb.client.MongoClient​​​​com.mongodb.client.MongoDatabase​​​​org.springframework.data.mongodb.core.MongoDatabaseFactory​

public interface MongoDatabaseFactory {

MongoDatabase getDatabase() throws DataAccessException;

MongoDatabase getDatabase(String dbName) throws DataAccessException;
}

以下部分介绍如何将容器与基于 Java 或基于 XML 的元数据一起使用来配置接口的实例。反过来,您可以使用实例进行配置。​​MongoDatabaseFactory​​​​MongoDatabaseFactory​​​​MongoTemplate​

您可以在标准 Java 代码中使用它们,而不是使用 IoC 容器来创建 MongoTemplate 的实例,如下所示:

public class MongoApp {

private static final Log log = LogFactory.getLog(MongoApp.class);

public static void main(String[] args) throws Exception {

MongoOperations mongoOps = new MongoTemplate(new SimpleMongoClientDatabaseFactory(MongoClients.create(), "database"));

mongoOps.insert(new Person("Joe", 34));

log.info(mongoOps.findOne(new Query(where("name").is("Joe")), Person.class));

mongoOps.dropCollection("person");
}
}

粗体代码突出显示了 和 的用法,这是入门部分中显示的列表之间的唯一区别。​​SimpleMongoClientDbFactory​

使用时选择作为选择的入口点。​​SimpleMongoClientDbFactory​​​​com.mongodb.client.MongoClient​

10.3.4. 注册​​MongoDatabaseFactory​

若要向容器注册实例,请编写与前面代码清单中突出显示的代码非常相似。下面的清单显示了一个简单的示例:​​MongoDatabaseFactory​

@Configuration
public class MongoConfiguration {

@Bean
public MongoDatabaseFactory mongoDatabaseFactory() {
return new SimpleMongoClientDatabaseFactory(MongoClients.create(), "database");
}
}

MongoDB服务器第3代在连接到数据库时更改了身份验证模型。因此,某些可用于身份验证的配置选项不再有效。应使用特定选项来设置凭据以提供身份验证数据,如以下示例所示:​​MongoClient​​​​MongoCredential​

爪哇岛

.XML

@Configuration
public class ApplicationContextEventTestsAppConfig extends AbstractMongoClientConfiguration {

@Override
public String getDatabaseName() {
return "database";
}

@Override
protected void configureClientSettings(Builder builder) {

builder
.credential(MongoCredential.createCredential("name", "db", "pwd".toCharArray()))
.applyToClusterSettings(settings -> {
settings.hosts(singletonList(new ServerAddress("127.0.0.1", 27017)));
});
}
}

当基于 XML 的配置中使用的用户名和密码凭据包含保留字符(如,,,或)时,必须对其进行 URL 编码。 以下示例显示了编码的凭据:→有关更多详细信息,请参阅RFC 3986 的第 2.2 节​。​​:​​​​%​​​​@​​​​,​​​​m0ng0@dmin:mo_res:bw6},Qsdxx@admin@database​​​​m0ng0%40dmin:mo_res%3Abw6%7D%2CQsdxx%40admin@database​

如果需要在用于创建 的实例上配置其他选项,则可以引用现有 Bean,如以下示例所示。为了显示另一种常见的使用模式,下面的清单显示了属性占位符的使用,该占位符允许您参数化配置和创建:​​com.mongodb.client.MongoClient​​​​SimpleMongoClientDbFactory​​​​MongoTemplate​

爪哇岛

.XML

@Configuration
@PropertySource("classpath:/com/myapp/mongodb/config/mongo.properties")
public class ApplicationContextEventTestsAppConfig extends AbstractMongoClientConfiguration {

@Autowired
Environment env;

@Override
public String getDatabaseName() {
return "database";
}

@Override
protected void configureClientSettings(Builder builder) {

builder.applyToClusterSettings(settings -> {
settings.hosts(singletonList(
new ServerAddress(env.getProperty("mongo.host"), env.getProperty("mongo.port", Integer.class))));
});

builder.applyToConnectionPoolSettings(settings -> {

settings.maxConnectionLifeTime(env.getProperty("mongo.pool-max-life-time", Integer.class), TimeUnit.MILLISECONDS)
.minSize(env.getProperty("mongo.pool-min-size", Integer.class))
.maxSize(env.getProperty("mongo.pool-max-size", Integer.class))
.maintenanceFrequency(10, TimeUnit.MILLISECONDS)
.maintenanceInitialDelay(11, TimeUnit.MILLISECONDS)
.maxConnectionIdleTime(30, TimeUnit.SECONDS)
.maxWaitTime(15, TimeUnit.MILLISECONDS);
});
}
}

10.4. 简介​​MongoTemplate​

Theclass位于软件包中,是Spring的MongoDB支持的核心类,并提供了丰富的功能集来与数据库进行交互。该模板提供了创建、更新、删除和查询 MongoDB 文档的便捷操作,并提供域对象和 MongoDB 文档之间的映射。​​MongoTemplate​​​​org.springframework.data.mongodb.core​

配置完成后,是线程安全的,可以在多个实例中重用。​​MongoTemplate​

MongoDB文档和域类之间的映射是通过委托给接口的实现来完成的。Spring 提供,但您也可以编写自己的转换器。有关更多详细信息,请参阅“自定义转化 - 覆盖默认映射”。​​MongoConverter​​​​MappingMongoConverter​

该类实现接口。尽可能以MongoDB驱动程序对象上可用的方法命名方法,以使习惯于驱动程序API的现有MongoDB开发人员熟悉API。例如,您可以找到,,,,,,,, 和等方法。设计目标是尽可能轻松地在使用基本MongoDB驱动程序和之间转换。这两个 API 之间的主要区别是可以传递域对象而不是。此外,具有流畅的 API,用于、和操作,而不是填充 a,以指定这些操作的参数。​​MongoTemplate​​​​MongoOperations​​​​MongoOperations​​​​Collection​​​​find​​​​findAndModify​​​​findAndReplace​​​​findOne​​​​insert​​​​remove​​​​save​​​​update​​​​updateMulti​​​​MongoOperations​​​​MongoOperations​​​​Document​​​​MongoOperations​​​​Query​​​​Criteria​​​​Update​​​​Document​

引用实例操作的首选方法是通过其接口。​​MongoTemplate​​​​MongoOperations​

默认转换器实现由 使用。虽然可以使用其他元数据来指定对象到文档的映射,但它也可以通过使用某些约定来转换不包含其他元数据的对象,以映射 ID 和集合名称。这些约定以及映射注释的使用在“映射”一章中进行了解释。​​MongoTemplate​​​​MappingMongoConverter​​​​MappingMongoConverter​

另一个核心功能是将MongoDB Java驱动程序抛出的异常转换为Spring的可移植数据访问异常层次结构。有关详细信息,请参阅“异常转换”。​​MongoTemplate​

​MongoTemplate​​提供了许多方便的方法,可帮助您轻松执行常见任务。但是,如果您需要直接访问MongoDB驱动程序API,则可以使用几种回调方法之一。回调为您提供对 aor aobject 的引用。有关详细信息,请参阅“执行回调”部分。​​Execute​​​​execute​​​​com.mongodb.client.MongoCollection​​​​com.mongodb.client.MongoDatabase​

下一节包含如何在 Spring 容器的上下文中使用的示例。​​MongoTemplate​

10.4.1. 实例化​​MongoTemplate​

可以使用以下配置来创建和注册 的实例,如以下示例所示:​​MongoTemplate​

例 57。注册对象并启用 Spring 的异常转换支持​​com.mongodb.client.MongoClient​

爪哇岛

.XML

@Configuration
class AppConfig {

@Bean
MongoClient mongoClient() {
return MongoClients.create("mongodb://localhost:27017");
}

@Bean
MongoTemplate mongoTemplate(MongoClient mongoClient) {
return new MongoTemplate(mongoClient, "geospatial");
}
}

有几个重载的构造函数:​​MongoTemplate​

  • ​MongoTemplate(MongoClient mongo, String databaseName)​​:获取要对其操作的对象和默认数据库名称。MongoClient
  • ​MongoTemplate(MongoDatabaseFactory mongoDbFactory)​​:采用封装对象、数据库名称以及用户名和密码的 MongoDbFactory 对象。MongoClient
  • ​MongoTemplate(MongoDatabaseFactory mongoDbFactory, MongoConverter mongoConverter)​​:添加用于映射的 ato。MongoConverter

创建时可能要设置的其他可选属性包括默认值、和属性。​​MongoTemplate​​​​WriteResultCheckingPolicy​​​​WriteConcern​​​​ReadPreference​


引用实例操作的首选方法是通过其接口。​​MongoTemplate​​​​MongoOperations​

10.4.2.政策​​WriteResultChecking​

在开发过程中,如果任何MongoDB操作包含错误,则记录或抛出异常非常方便。在开发过程中忘记执行此操作,然后最终得到一个看起来成功运行的应用程序是很常见的,而实际上,数据库没有按照您的期望进行修改。可以将属性 of 设置为以下值之一:或者,分别抛出 anor 不执行任何操作。默认值是使用 avalue of。​​com.mongodb.WriteResult​​​​WriteResultChecking​​​​MongoTemplate​​​​EXCEPTION​​​​NONE​​​​Exception​​​​WriteResultChecking​​​​NONE​

10.4.3. ​​WriteConcern​

如果尚未通过驱动程序在更高级别(例如)指定,则可以设置用于写入操作的属性。如果未设置此属性,则默认为 MongoDB 驱动程序的数据库或集合设置中设置的属性。​​com.mongodb.client.MongoClient​​​​com.mongodb.WriteConcern​​​​MongoTemplate​​​​WriteConcern​

10.4.4. ​​WriteConcernResolver​

对于您希望在每个操作的基础上设置不同值的更高级情况(用于删除、更新、插入和保存操作),可以配置调用的策略接口。由于用于持久化 POJO,因此允许您创建一个策略,该策略可以将特定的 POJO 类映射到 avalue。以下清单显示了界面:​​WriteConcern​​​​WriteConcernResolver​​​​MongoTemplate​​​​MongoTemplate​​​​WriteConcernResolver​​​​WriteConcern​​​​WriteConcernResolver​

public interface WriteConcernResolver {
WriteConcern resolve(MongoAction action);
}

可以使用参数来确定值或使用模板本身的值作为默认值。包含要写入的集合名称、POJO、转换的集合名称、操作 (,,,, or) 以及其他一些上下文信息。下面的示例演示两组类获得不同的设置:​​MongoAction​​​​WriteConcern​​​​MongoAction​​​​java.lang.Class​​​​Document​​​​REMOVE​​​​UPDATE​​​​INSERT​​​​INSERT_LIST​​​​SAVE​​​​WriteConcern​

private class MyAppWriteConcernResolver implements WriteConcernResolver {

public WriteConcern resolve(MongoAction action) {
if (action.getEntityClass().getSimpleName().contains("Audit")) {
return WriteConcern.NONE;
} else if (action.getEntityClass().getSimpleName().contains("Metadata")) {
return WriteConcern.JOURNAL_SAFE;
}
return action.getDefaultWriteConcern();
}
}

10.5. 保存、更新和删除文档

​MongoTemplate​​允许您保存、更新和删除域对象,并将这些对象映射到存储在 MongoDB 中的文档。

请考虑以下类:

public class Person {

private String id;
private String name;
private int age;

public Person(String name, int age) {
this.name = name;
this.age = age;
}

public String getId() {
return id;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}

@Override
public String toString() {
return "Person [id=" + id + ", name=" + name + ", age=" + age + "]";
}

}

给定前面示例中的类,您可以保存、更新和删除对象,如以下示例所示:​​Person​


​MongoOperations​​​是实现的接口。​​MongoTemplate​

package org.spring.example;

public class MongoApp {

private static final Log log = LogFactory.getLog(MongoApp.class);

public static void main(String[] args) {

MongoOperations mongoOps = new MongoTemplate(new SimpleMongoClientDbFactory(MongoClients.create(), "database"));

Person p = new Person("Joe", 34);

// Insert is used to initially store the object into the database.
mongoOps.insert(p);
log.info("Insert: " + p);

// Find
p = mongoOps.findById(p.getId(), Person.class);
log.info("Found: " + p);

// Update
mongoOps.updateFirst(query(where("name").is("Joe")), update("age", 35), Person.class);
p = mongoOps.findOne(query(where("name").is("Joe")), Person.class);
log.info("Updated: " + p);

// Delete
mongoOps.remove(p);

// Check that deletion worked
List<Person> people = mongoOps.findAll(Person.class);
log.info("Number of people = : " + people.size());


mongoOps.dropCollection(Person.class);
}
}

前面的示例将生成以下日志输出(包括来自的调试消息):​​MongoTemplate​

DEBUG apping.MongoPersistentEntityIndexCreator:  80 - Analyzing class class org.spring.example.Person for index information.
DEBUG work.data.mongodb.core.MongoTemplate: 632 - insert Document containing fields: [_class, age, name] in collection: person
INFO org.spring.example.MongoApp: 30 - Insert: Person [id=4ddc6e784ce5b1eba3ceaf5c, name=Joe, age=34]
DEBUG work.data.mongodb.core.MongoTemplate:1246 - findOne using query: { "_id" : { "$oid" : "4ddc6e784ce5b1eba3ceaf5c"}} in db.collection: database.person
INFO org.spring.example.MongoApp: 34 - Found: Person [id=4ddc6e784ce5b1eba3ceaf5c, name=Joe, age=34]
DEBUG work.data.mongodb.core.MongoTemplate: 778 - calling update using query: { "name" : "Joe"} and update: { "$set" : { "age" : 35}} in collection: person
DEBUG work.data.mongodb.core.MongoTemplate:1246 - findOne using query: { "name" : "Joe"} in db.collection: database.person
INFO org.spring.example.MongoApp: 39 - Updated: Person [id=4ddc6e784ce5b1eba3ceaf5c, name=Joe, age=35]
DEBUG work.data.mongodb.core.MongoTemplate: 823 - remove using query: { "id" : "4ddc6e784ce5b1eba3ceaf5c"} in collection: person
INFO org.spring.example.MongoApp: 46 - Number of people = : 0
DEBUG work.data.mongodb.core.MongoTemplate: 376 - Dropped collection [database.person]

​MongoConverter​​通过识别(通过约定)属性名称,导致 aand anstored 在数据库中的隐式转换。​​String​​​​ObjectId​​​​Id​

前面的示例旨在显示对 Save 、更新和删除操作的使用,而不是显示复杂的映射功能。​​MongoTemplate​

前面的示例中使用的查询语法在“查询文档”一节中有更详细的说明。

10.5.1. 如何在映射层中处理字段​​_id​

MongoDB要求你对所有文档都有anfield。如果未提供,驱动程序将分配 anwith 生成的值。使用 时,某些规则控制如何将 Java 类的属性映射到此字段:​​_id​​​​ObjectId​​​​MappingMongoConverter​​​​_id​

  1. 用 () 注释的属性或字段映射到字段。@Idorg.springframework.data.annotation.Id_id
  2. 没有批注但已命名的属性或字段映射到字段。id_id

下面概述了在使用 (默认值) 时对映射到文档字段的属性执行的类型转换(如果有)。​​_id​​​​MappingMongoConverter​​​​MongoTemplate​

  1. 如果可能的话,声明为 Java 类中的属性或字段将使用 Spring 转换为 anby 并存储为。有效的转换规则委托给MongoDB Java驱动程序。如果无法转换为 an,则该值将作为字符串存储在数据库中。idStringObjectIdConverter<String, ObjectId>ObjectId
  2. 在 Java 类中声明的属性或字段使用 Spring 转换为 anby 并存储为 anby。idBigIntegerObjectIdConverter<BigInteger, ObjectId>

如果 Java 类中不存在前面的规则集中指定的字段或属性,则驱动程序会生成隐式文件,但不会映射到 Java 类的属性或字段。​​_id​

查询和更新时,使用与上述规则相对应的转换器来保存文档,以便查询中使用的字段名称和类型可以与域类中的字段名称和类型匹配。​​MongoTemplate​

某些环境需要自定义的映射值方法,例如存储在MongoDB中的数据,这些数据未通过Spring Data映射层运行。文档可以包含可以表示为 asor 的值。 将文档从存储读取回域类型即可。由于隐式转换,通过他们的查询文档可能很麻烦。因此,无法以这种方式检索文档。 对于这些情况,提供对实际 id 映射尝试的更多控制。​​Id​​​​_id​​​​ObjectId​​​​String​​​​id​​​​ObjectId​​​​@MongoId​

例 58.映射​​@MongoId​

public class PlainStringId {
@MongoId String id;
}

public class PlainObjectId {
@MongoId ObjectId id;
}

public class StringToObjectId {
@MongoId(FieldType.OBJECT_ID) String id;
}

该 id 被视为无需进一步转换。​​String​

该 id 被视为。​​ObjectId​

id 被视为给定是有效的十六进制,否则视为。对应于用法。​​ObjectId​​​​String​​​​ObjectId​​​​String​​​​@Id​

10.5.2. 类型映射

MongoDB集合可以包含表示各种类型的实例的文档。如果存储类的层次结构或具有具有类型属性的类,则此功能非常有用。在后一种情况下,检索对象时必须正确读取该属性中保存的值。因此,我们需要一种机制来将类型信息与实际文档一起存储。​​Object​

为了实现这一点,使用抽象作为其主要实现。它的默认行为是在文档中存储完全限定的类名。类型提示是为*文档以及每个值(如果它是复杂类型和声明的属性类型的子类型)编写的。以下示例(末尾带有 JSON 表示形式)显示了映射的工作原理:​​MappingMongoConverter​​​​MongoTypeMapper​​​​DefaultMongoTypeMapper​​​​_class​

例 59。类型映射

class Sample {
Contact value;
}

abstract class Contact { … }

class Person extends Contact { … }

Sample sample = new Sample();
sample.value = new Person();

mongoTemplate.save(sample);

{
"value" : { "_class" : "com.acme.Person" },
"_class" : "com.acme.Sample"
}

Spring Data MongoDB将类型信息存储为实际根类以及嵌套类型的最后一个字段(因为它很复杂并且是子类型)。因此,如果您现在使用,则可以发现存储的文档是实例。您还可以发现 value 属性实际上是 a。​​Contact​​​​mongoTemplate.findAll(Object.class, "sample")​​​​Sample​​​​Person​

自定义类型映射

如果要避免将整个 Java 类名编写为类型信息,而是希望使用键,则可以在实体类上使用注释。如果您需要进一步自定义映射,请查看界面。可以在 上配置该接口的实例,而该实例又可以配置在 上。下面的示例演示如何为实体定义类型别名:​​@TypeAlias​​​​TypeInformationMapper​​​​DefaultMongoTypeMapper​​​​MappingMongoConverter​

例 60。定义实体的类型别名

@TypeAlias("pers")
class Person {

}

请注意,生成的文档包含字段中的值。​​pers​​​​_class​


仅当映射上下文知道实际类型时,类型别名才有效。 所需的实体元数据在首次保存时确定,或者必须通过配置初始实体集提供。 默认情况下,配置类会扫描基本包以查找潜在的候选项。




@Configuration
class AppConfig extends AbstractMongoClientConfiguration {

@Override
protected Set<Class<?>> getInitialEntitySet() {
return Collections.singleton(Person.class);
}

// ...
}



配置自定义类型映射

以下示例演示如何配置自定义:​​MongoTypeMapper​​​​MappingMongoConverter​

class CustomMongoTypeMapper extends DefaultMongoTypeMapper {
//implement custom type mapping here
}

例 61。配置自定义​​MongoTypeMapper​

爪哇岛

.XML

@Configuration
class SampleMongoConfiguration extends AbstractMongoClientConfiguration {

@Override
protected String getDatabaseName() {
return "database";
}

@Bean
@Override
public MappingMongoConverter mappingMongoConverter(MongoDatabaseFactory databaseFactory,
MongoCustomConversions customConversions, MongoMappingContext mappingContext) {
MappingMongoConverter mmc = super.mappingMongoConverter();
mmc.setTypeMapper(customTypeMapper());
return mmc;
}

@Bean
public MongoTypeMapper customTypeMapper() {
return new CustomMongoTypeMapper();
}
}

请注意,前面的示例扩展了类并覆盖了我们配置自定义的位置的 Bean 定义。​​AbstractMongoClientConfiguration​​​​MappingMongoConverter​​​​MongoTypeMapper​

10.5.3. 保存和插入文档的方法

有几种方便的方法用于保存和插入对象。为了对转换过程进行更精细的控制,您可以注册 Spring 转换器 例如。​​MongoTemplate​​​​MappingMongoConverter​​​​Converter<Person, Document>​​​​Converter<Document, Person>​

插入操作和保存操作之间的区别在于,如果对象尚不存在,则保存操作将执行插入。

使用保存操作的简单情况是保存 POJO。在这种情况下,集合名称由类的名称(不完全限定)确定。您还可以使用特定的集合名称调用保存操作。可以使用映射元数据来覆盖要在其中存储对象的集合。

插入或保存时,如果未设置属性,则假定其值将由数据库自动生成。因此,要使 anto 的自动生成成功,类中的属性或字段的类型必须是 a、an 或 a。​​Id​​​​ObjectId​​​​Id​​​​String​​​​ObjectId​​​​BigInteger​

下面的示例演示如何保存文档并检索其内容:

例 62。使用 MongoTemplate 插入和检索文档



Person p = new Person("Bob", 33);
mongoTemplate.insert(p);

Person qp = mongoTemplate.findOne(query(where("age").is(33)), Person.class);

可以使用以下插入和保存操作:

  • ​void​​ 保存:将对象保存到默认集合。(Object objectToSave)
  • ​void​​ 保存:将对象保存到指定的集合。(Object objectToSave, String collectionName)

还提供了一组类似的插入操作:

  • ​void​​ 插入:将对象插入到默认集合。(Object objectToSave)
  • ​void​​ 插入:将对象插入到指定的集合。(Object objectToSave, String collectionName)
我的文档保存到哪个集合中?

有两种方法可以管理用于文档的集合名称。使用的默认集合名称是更改为以小写字母开头的类名。所以 aclass 存储在集合中。您可以通过提供带有注释的不同集合名称来自定义此设置。还可以通过将自己的集合名称作为所选方法调用的最后一个参数来覆盖集合名称。​​com.test.Person​​​​person​​​​@Document​​​​MongoTemplate​

插入或保存单个对象

MongoDB驱动程序支持在单个操作中插入文档集合。接口中的以下方法支持此功能:​​MongoOperations​

  • 插入:插入对象。如果存在具有相同内容的现有文档,则会生成错误。id
  • insertAll:将 aof 对象作为第一个参数。此方法检查每个对象,并根据前面指定的规则将其插入到相应的集合中。Collection
  • 保存:保存对象,覆盖可能具有相同对象的任何对象。id
在批处理中插入多个对象

MongoDB驱动程序支持在一个操作中插入文档集合。接口中的以下方法支持此功能:​​MongoOperations​

  • 插入方法:取 aas 第一个参数。它们在对数据库的单个批处理写入中插入对象列表。Collection

10.5.4. 更新集合中的文档

对于更新,您可以更新使用 找到的第一个文档,也可以使用该方法更新找到的所有文档以匹配查询。以下示例显示了所有帐户的更新,其中我们使用运算符将一次性 $50.00 奖金添加到余额中:​​MongoOperation.updateFirst​​​​MongoOperation.updateMulti​​​​SAVINGS​​​​$inc​

例 63。使用​​MongoTemplate​

...

WriteResult wr = mongoTemplate.updateMulti(new Query(where("accounts.accountType").is(Account.Type.SAVINGS)),
new Update().inc("accounts.$.balance", 50.00), Account.class);

除了前面讨论的之外,我们还使用 anobject 提供了更新定义。该类具有与可用于MongoDB的更新修饰符匹配的方法。​​Query​​​​Update​​​​Update​

大多数方法返回对象,以便为 API 提供流畅的样式。​​Update​

运行文档更新的方法
  • updateFirst:使用更新的文档更新与查询文档条件匹配的第一个文档。
  • updateMulti:使用更新的文档更新与查询文档条件匹配的所有对象。

​updateFirst​​​不支持排序。请使用查找和修改​来申请。​​Sort​

类中的方法​​Update​

你可以在 theclass 中使用一点“语法糖”,因为它的方法应该链接在一起。此外,您可以通过使用静态导入来启动新实例的创建。​​Update​​​​Update​​​​public static Update update(String key, Object value)​

该类包含以下方法:​​Update​

  • ​Update​​ 添加到集使用更新修改器更新(String key, Object value)$addToSet
  • ​Update​​ 当前日期使用更新修改器更新(String key)$currentDate
  • ​Update​​ 当前时间戳使用更新修饰符进行更新(String key)$currentDate$type timestamp
  • ​Update​​ 公司使用更新修改器更新(String key, Number inc)$inc
  • ​Update​​ .max使用更新修改器更新(String key, Object max)$max
  • ​Update​​ 最小使用更新修改器更新(String key, Object min)$min
  • ​Update​​ 使用更新修改器更新(String key, Number multiplier)$mul
  • ​Update​​ 流行使用更新修改器更新(String key, Update.Position pos)$pop
  • ​Update​​ 使用更新修改器更新(String key, Object value)$pull
  • ​Update​​ 拉全部使用更新修改器更新(String key, Object[] values)$pullAll
  • ​Update​​ 使用更新修改器更新(String key, Object value)$push
  • ​Update​​ 推送全部使用更新修改器更新(String key, Object[] values)$pushAll
  • ​Update​​ 重命名使用更新修改器更新(String oldName, String newName)$rename
  • ​Update​​ 设置使用更新修改器更新(String key, Object value)$set
  • ​Update​​ 设置插入使用更新修改器更新(String key, Object value)$setOnInsert
  • ​Update​​ 未凝固的使用更新修改器更新(String key)$unset

某些更新修饰符(如 and)允许嵌套其他运算符。​​$push​​​​$addToSet​

// { $push : { "category" : { "$each" : [ "spring" , "data" ] } } }
new Update().push("category").each("spring", "data")

// { $push : { "key" : { "$position" : 0 , "$each" : [ "Arya" , "Arry" , "Weasel" ] } } }
new Update().push("key").atPosition(Position.FIRST).each(Arrays.asList("Arya", "Arry", "Weasel"));

// { $push : { "key" : { "$slice" : 5 , "$each" : [ "Arya" , "Arry" , "Weasel" ] } } }
new Update().push("key").slice(5).each(Arrays.asList("Arya", "Arry", "Weasel"));

// { $addToSet : { "values" : { "$each" : [ "spring" , "data" , "mongodb" ] } } }
new Update().addToSet("values").each("spring", "data", "mongodb");

10.5.5. “更新插入”集合中的文档

与执行操作相关,您还可以执行“upsert”操作,如果未找到与查询匹配的文档,该操作将执行插入。插入的文档是查询文档和更新文档的组合。下面的示例演示如何使用该方法:​​updateFirst​​​​upsert​

template.update(Person.class)
.matching(query(where("ssn").is(1111).and("firstName").is("Joe").and("Fraizer").is("Update"))
.apply(update("address", addr))
.upsert();

​upsert​​​不支持排序。请使用查找和修改​来申请。​​Sort​

10.5.6. 查找和更新集合中的文档

该方法可以在单个操作中更新文档并返回旧的或新更新的文档。提供了四个重载的方法,这些方法可以获取和类转换到 POJO:​​findAndModify(…)​​​​MongoCollection​​​​MongoTemplate​​​​findAndModify​​​​Query​​​​Update​​​​Document​

<T> T findAndModify(Query query, Update update, Class<T> entityClass);

<T> T findAndModify(Query query, Update update, Class<T> entityClass, String collectionName);

<T> T findAndModify(Query query, Update update, FindAndModifyOptions options, Class<T> entityClass);

<T> T findAndModify(Query query, Update update, FindAndModifyOptions options, Class<T> entityClass, String collectionName);

下面的示例将几个对象插入到容器中并执行操作:​​Person​​​​findAndUpdate​

template.insert(new Person("Tom", 21));
template.insert(new Person("Dick", 22));
template.insert(new Person("Harry", 23));

Query query = new Query(Criteria.where("firstName").is("Harry"));
Update update = new Update().inc("age", 1);

Person oldValue = template.update(Person.class)
.matching(query)
.apply(update)
.findAndModifyValue(); // return's old person object

assertThat(oldValue.getFirstName()).isEqualTo("Harry");
assertThat(oldValue.getAge()).isEqualTo(23);

Person newValue = template.query(Person.class)
.matching(query)
.findOneValue();

assertThat(newValue.getAge()).isEqualTo(24);

Person newestValue = template.update(Person.class)
.matching(query)
.apply(update)
.withOptions(FindAndModifyOptions.options().returnNew(true)) // Now return the newly updated document when updating
.findAndModifyValue();

assertThat(newestValue.getAge()).isEqualTo(25);

该方法允许您设置、和的选项。下面是从前面的代码片段扩展的示例:​​FindAndModifyOptions​​​​returnNew​​​​upsert​​​​remove​

Person upserted = template.update(Person.class)
.matching(new Query(Criteria.where("firstName").is("Mary")))
.apply(update)
.withOptions(FindAndModifyOptions.options().upsert(true).returnNew(true))
.findAndModifyValue()

assertThat(upserted.getFirstName()).isEqualTo("Mary");
assertThat(upserted.getAge()).isOne();

10.5.7. 聚合管道更新

更新方法也通过以下方式接受聚合管道。 使用允许在更新操作中利用MongoDB 4.2聚合。 在更新中使用聚合允许通过单个操作表达多个阶段和多个条件来更新一个或多个字段。​​MongoOperations​​​​ReactiveMongoOperations​​​​AggregationUpdate​​​​AggregationUpdate​

更新可以包括以下阶段:

  • ​AggregationUpdate.set(…).toValue(…)​​ → $set : { …​ }
  • ​AggregationUpdate.unset(…)​​ → $unset : [ …​ ]
  • ​AggregationUpdate.replaceWith(…)​​ → $replaceWith : { …​ }

例 64。更新聚合

AggregationUpdate update = Aggregation.newUpdate()
.set("average").toValue(ArithmeticOperators.valueOf("tests").avg())
.set("grade").toValue(ConditionalOperators.switchCases(
when(valueOf("average").greaterThanEqualToValue(90)).then("A"),
when(valueOf("average").greaterThanEqualToValue(80)).then("B"),
when(valueOf("average").greaterThanEqualToValue(70)).then("C"),
when(valueOf("average").greaterThanEqualToValue(60)).then("D"))
.defaultTo("F")
);

template.update(Student.class)
.apply(update)
.all();
db.students.update(                                                         
{ },
[
{ $set: { average : { $avg: "$tests" } } },
{ $set: { grade: { $switch: {
branches: [
{ case: { $gte: [ "$average", 90 ] }, then: "A" },
{ case: { $gte: [ "$average", 80 ] }, then: "B" },
{ case: { $gte: [ "$average", 70 ] }, then: "C" },
{ case: { $gte: [ "$average", 60 ] }, then: "D" }
],
default: "F"
} } } }
],
{ multi: true }
)

第一阶段根据测试字段的平均值计算新的字段平均值。​​$set​

第二阶段根据第一个聚合阶段计算的平均油田计算出新的油田等级。​​$set​

管道在学生集合上运行,并用于聚合字段映射。​​Student​

将更新应用于集合中的所有匹配文档。

10.5.8. 查找和替换文档

最直接的替换整体的方法就是使用该方法。然而,这 可能并不总是可行的。提供了一种替代方法,允许通过以下方式标识要替换的文档 一个简单的查询。​​Document​​​​id​​​​save​​​​findAndReplace​

例 65。查找和替换文档

Optional<User> result = template.update(Person.class)      
.matching(query(where("firstame").is("Tom")))
.replaceWith(new Person("Dick"))
.withOptions(FindAndReplaceOptions.options().upsert())
.as(User.class)
.findAndReplace();

将流畅更新 API 与给定的域类型一起使用,以映射查询并派生集合名称,或者仅使用。​​MongoOperations#findAndReplace​

针对给定域类型映射的实际匹配查询。通过查询提供和设置。​​sort​​​​fields​​​​collation​

额外的可选钩子,用于提供默认值以外的选项,例如。​​upsert​

用于映射操作结果的可选投影类型。如果没有给定的初始域类型,则使用初始域类型。

触发实际处理。用于获取可为空的结果,而不是 an。​​findAndReplaceValue​​​​Optional​

请注意,替换者不得持有自己,因为现有的将是 由商店本身进行更换。还要记住,这只会取代第一个 根据可能给定的排序顺序与查询条件匹配的文档。​​id​​​​id​​​​Document​​​​findAndReplace​

10.5.9. 删除文档的方法

可以使用以下五种重载方法之一从数据库中删除对象:

template.remove(tywin, "GOT");                                              

template.remove(query(where("lastname").is("lannister")), "GOT");

template.remove(new Query().limit(3), "GOT");

template.findAllAndRemove(query(where("lastname").is("lannister"), "GOT");

template.findAllAndRemove(new Query().limit(3), "GOT");

从关联的集合中删除其指定的单个实体。​​_id​

从集合中删除与查询条件匹配的所有文档。​​GOT​

删除集合中的前三个文档。与 <2> 不同,要删除的文档由他们的文档标识,首先运行给定的查询、应用 、 和选项,然后在单独的步骤中一次性删除所有文档。​​GOT​​​​_id​​​​sort​​​​limit​​​​skip​

从集合中删除与查询条件匹配的所有文档。与 <3> 不同,文档不会批量删除,而是逐个删除。​​GOT​

删除集合中的前三个文档。与 <3> 不同,文档不会批量删除,而是逐个删除。​​GOT​

10.5.10. 乐观锁定

Theannotation提供了类似于MongoDB上下文中的JPA语法,并确保更新仅应用于具有匹配版本的文档。因此,版本属性的实际值将添加到更新查询中,这样,如果同时另一个操作更改了文档,则更新不会产生任何影响。在这种情况下,茴香扔了。以下示例显示了这些功能:​​@Version​​​​OptimisticLockingFailureException​

@Document
class Person {

@Id String id;
String firstname;
String lastname;
@Version Long version;
}

Person daenerys = template.insert(new Person("Daenerys"));

Person tmp = template.findOne(query(where("id").is(daenerys.getId())), Person.class);

daenerys.setLastname("Targaryen");
template.save(daenerys);

template.save(tmp); // throws OptimisticLockingFailureException

插入 document.is 设置为。​​version​​​​0​

加载刚刚插入的 document.is。​​version​​​​0​

更新文档。设置和碰撞。​​version = 0​​​​lastname​​​​version​​​​1​

尝试更新以前加载的文档。操作失败,并显示 ,因为电流是。​​version = 0​​​​OptimisticLockingFailureException​​​​version​​​​1​

乐观锁定需要设置 theto。否则可以默默吞咽。​​WriteConcern​​​​ACKNOWLEDGED​​​​OptimisticLockingFailureException​

从版本 2.2 开始,从数据库中删除实体时还包括属性。 要删除没有版本检查使用代替。​​MongoOperations​​​​@Version​​​​Document​​​​MongoOperations#remove(Query,…)​​​​MongoOperations#remove(Object)​

从版本 2.2 开始,存储库在删除受版本控制的实体时检查确认删除的结果。 如果无法通过删除版本化实体,则引发 Anis。在这种情况下,版本已更改或对象同时被删除。用于绕过乐观锁定功能并删除对象,而不管其版本如何。​​OptimisticLockingFailureException​​​​CrudRepository.delete(Object)​​​​CrudRepository.deleteById(ID)​