对于最新的稳定版本,请使用 Spring Data Relational 3.3.1Spring中文文档

对于最新的稳定版本,请使用 Spring Data Relational 3.3.1Spring中文文档

R2dbcEntityTemplate是 Spring Data R2DBC 的中心入口点。 它为典型的临时用例(如查询、插入、更新和删除数据)提供了直接的面向实体的方法和更窄、更流畅的界面。Spring中文文档

入口点(、、 等)遵循基于要运行的操作的自然命名架构。 从入口点开始,API 旨在仅提供上下文相关的方法,这些方法会导致创建和运行 SQL 语句的终止方法。 Spring Data R2DBC 使用抽象来确定绑定标记、分页支持和基础驱动程序本机支持的数据类型。insert()select()update()R2dbcDialectSpring中文文档

所有终端方法始终返回表示所需操作的类型。 实际语句在订阅时发送到数据库。Publisher
所有终端方法始终返回表示所需操作的类型。 实际语句在订阅时发送到数据库。Publisher

插入和更新实体的方法

有几种方便的方法可以保存和插入对象。 为了对转换过程进行更精细的控制,您可以注册 Spring 转换器,例如 和 。R2dbcEntityTemplateR2dbcCustomConversionsConverter<Person, OutboundRow>Converter<Row, Person>Spring中文文档

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

插入或保存时,如果未设置该属性,则假定其值将由数据库自动生成。 因此,对于自动生成,类中的属性或字段的类型必须是 或 。IdIdLongIntegerSpring中文文档

下面的示例演示如何插入行并检索其内容:Spring中文文档

使用R2dbcEntityTemplate
Person person = new Person("John", "Doe");

Mono<Person> saved = template.insert(person);
Mono<Person> loaded = template.selectOne(query(where("firstname").is("John")),
		Person.class);

可以使用以下插入和更新操作:Spring中文文档

此外,还提供一组类似的插入操作:Spring中文文档

可以使用 Fluent API 自定义表名。Spring中文文档

选择数据

和 方法用于从表中选择数据。 这两种方法都采用一个 Query 对象,该对象定义字段投影、子句、子句和限制/偏移分页。 无论基础数据库如何,限制/偏移功能对应用程序都是透明的。 R2dbcDialect 抽象支持此功能,以满足各个 SQL 风格之间的差异。select(…)selectOne(…)R2dbcEntityTemplateWHEREORDER BYSpring中文文档

使用R2dbcEntityTemplate
Flux<Person> loaded = template.select(query(where("firstname").is("John")),
		Person.class);

Fluent API

本节介绍 Fluent API 的用法。 请考虑以下简单查询:Spring中文文档

Flux<Person> people = template.select(Person.class) (1)
		.all(); (2)
1 与方法一起使用,将表格结果映射到结果对象上。Personselect(…)Person
2 提取行将返回 a 而不限制结果。all()Flux<Person>

下面的示例声明一个更复杂的查询,该查询按名称、条件和子句指定表名:WHEREORDER BYSpring中文文档

Mono<Person> first = template.select(Person.class)	(1)
	.from("other_person")
	.matching(query(where("firstname").is("John")			(2)
		.and("lastname").in("Doe", "White"))
	  .sort(by(desc("id"))))													(3)
	.one();																						(4)
1 按名称从表中进行选择将返回使用给定域类型的行结果。
2 发出的查询声明 on 的条件和列以筛选结果。WHEREfirstnamelastname
3 结果可以按单个列名排序,从而形成一个子句。ORDER BY
4 选择一个结果仅获取一行。 这种使用行的方式要求查询只返回一个结果。 如果查询生成多个结果,则发出 。MonoIncorrectResultSizeDataAccessException
您可以通过 提供目标类型来直接将投影应用于结果。select(Class<?>)

您可以通过以下终止方法在检索单个实体和检索多个实体之间切换:Spring中文文档

  • first():仅使用第一行,返回 . 如果查询未返回任何结果,则返回的将完成而不发出对象。MonoMonoSpring中文文档

  • one():只消耗一行,返回 . 如果查询未返回任何结果,则返回的将完成而不发出对象。 如果查询返回多行,则完成异常发出 。MonoMonoMonoIncorrectResultSizeDataAccessExceptionSpring中文文档

  • all():使用返回 .FluxSpring中文文档

  • count():应用返回 的计数投影。Mono<Long>Spring中文文档

  • exists():通过返回 来返回查询是否产生任何行。Mono<Boolean>Spring中文文档

您可以使用入口点来表达您的查询。 生成的查询支持常用子句 ( 和 ) 并支持分页。 流畅的 API 风格可让您将多个方法链接在一起,同时拥有易于理解的代码。 为了提高可读性,您可以使用静态导入来避免使用“new”关键字来创建实例。select()SELECTSELECTWHEREORDER BYCriteriaSpring中文文档

Criteria 类的方法

该类提供以下方法,所有这些方法都对应于 SQL 运算符:CriteriaSpring中文文档

  • Criteria :将具有指定值的链式链接添加到当前,并返回新创建的链式链接。(String column)CriteriapropertyCriteriaSpring中文文档

  • Criteria :将具有指定值的链式链接添加到当前,并返回新创建的链式链接。(String column)CriteriapropertyCriteriaSpring中文文档

  • Criteria greaterThan :使用运算符创建条件。(Object o)>Spring中文文档

  • Criteria greaterThanOrEquals :使用运算符创建条件。(Object o)>=Spring中文文档

  • Criteria in : 通过使用 varargs 参数的运算符创建条件。(Object…​ o)INSpring中文文档

  • Criteria in :通过使用集合的运算符创建条件。(Collection<?> collection)INSpring中文文档

  • Criteria is :使用列匹配 () 创建条件。(Object o)property = valueSpring中文文档

  • Criteria isNull :使用运算符创建条件。()IS NULLSpring中文文档

  • Criteria isNotNull :使用运算符创建条件。()IS NOT NULLSpring中文文档

  • Criteria lessThan :使用运算符创建条件。(Object o)<Spring中文文档

  • Criteria lessThanOrEquals :使用运算符创建条件。(Object o)Spring中文文档

  • Criteria like :使用运算符创建条件,而不进行转义字符处理。(Object o)LIKESpring中文文档

  • Criteria not :使用运算符创建条件。(Object o)!=Spring中文文档

  • Criteria notIn :通过使用 varargs 参数的运算符创建条件。(Object…​ o)NOT INSpring中文文档

  • Criteria notIn :通过使用集合的运算符创建条件。(Collection<?> collection)NOT INSpring中文文档

可以与 、 和 查询一起使用。CriteriaSELECTUPDATEDELETESpring中文文档

1 与方法一起使用,将表格结果映射到结果对象上。Personselect(…)Person
2 提取行将返回 a 而不限制结果。all()Flux<Person>
1 按名称从表中进行选择将返回使用给定域类型的行结果。
2 发出的查询声明 on 的条件和列以筛选结果。WHEREfirstnamelastname
3 结果可以按单个列名排序,从而形成一个子句。ORDER BY
4 选择一个结果仅获取一行。 这种使用行的方式要求查询只返回一个结果。 如果查询生成多个结果,则发出 。MonoIncorrectResultSizeDataAccessException
您可以通过 提供目标类型来直接将投影应用于结果。select(Class<?>)

插入数据

您可以使用入口点插入数据。insert()Spring中文文档

请考虑以下简单的类型化插入操作:Spring中文文档

Mono<Person> insert = template.insert(Person.class)	(1)
		.using(new Person("John", "Doe")); (2)
1 与方法一起使用,根据映射元数据设置表。 它还准备 insert 语句以接受要插入的对象。Personinto(…)INTOPerson
2 提供标量对象。 或者,您可以提供 a 来运行语句流。 此方法提取所有非值并插入它们。PersonPublisherINSERTnull
1 与方法一起使用,根据映射元数据设置表。 它还准备 insert 语句以接受要插入的对象。Personinto(…)INTOPerson
2 提供标量对象。 或者,您可以提供 a 来运行语句流。 此方法提取所有非值并插入它们。PersonPublisherINSERTnull

更新数据

您可以使用入口点更新行。 更新数据首先通过接受指定分配来指定要更新的表。 它还接受创建子句。update()UpdateQueryWHERESpring中文文档

请考虑以下简单的类型化更新操作:Spring中文文档

Person modified = …

		Mono<Long> update = template.update(Person.class)	(1)
				.inTable("other_table")														(2)
				.matching(query(where("firstname").is("John")))		(3)
				.apply(update("age", 42));												(4)
1 更新对象并应用基于映射元数据的映射。Person
2 通过调用该方法设置不同的表名。inTable(…)
3 指定转换为子句的查询。WHERE
4 应用对象。 在本例中,将 设置为 并返回受影响的行数。Updateage42
1 更新对象并应用基于映射元数据的映射。Person
2 通过调用该方法设置不同的表名。inTable(…)
3 指定转换为子句的查询。WHERE
4 应用对象。 在本例中,将 设置为 并返回受影响的行数。Updateage42

删除数据

您可以使用入口点删除行。 删除数据从要删除的表的规范开始,并可以选择接受创建子句。delete()CriteriaWHERESpring中文文档

请考虑以下简单的插入操作:Spring中文文档

		Mono<Long> delete = template.delete(Person.class)	(1)
				.from("other_table")															(2)
				.matching(query(where("firstname").is("John")))		(3)
				.all();																						(4)
1 删除对象并基于映射元数据应用映射。Person
2 通过调用该方法设置不同的表名。from(…)
3 指定转换为子句的查询。WHERE
4 应用删除操作并返回受影响的行数。

使用 Repositories,可以使用该方法执行保存实体。 如果实体是新的,则会导致该实体的插入。ReactiveCrudRepository.save(…)Spring中文文档

如果实体不是新的,则会更新。 请注意,实例是否为新实例是实例状态的一部分。Spring中文文档

这种方法有一些明显的缺点。 如果实际更改的引用实体中只有很少的实体,则删除和插入是浪费。 虽然这个过程可以而且可能会得到改进,但Spring Data R2DBC可以提供的东西有一定的局限性。 它不知道聚合的先前状态。 因此,任何更新过程都必须获取它在数据库中找到的任何内容,并确保将其转换为传递给 save 方法的实体的任何状态。
1 删除对象并基于映射元数据应用映射。Person
2 通过调用该方法设置不同的表名。from(…)
3 指定转换为子句的查询。WHERE
4 应用删除操作并返回受影响的行数。
这种方法有一些明显的缺点。 如果实际更改的引用实体中只有很少的实体,则删除和插入是浪费。 虽然这个过程可以而且可能会得到改进,但Spring Data R2DBC可以提供的东西有一定的局限性。 它不知道聚合的先前状态。 因此,任何更新过程都必须获取它在数据库中找到的任何内容,并确保将其转换为传递给 save 方法的实体的任何状态。

ID生成

Spring Data 使用 identifer 属性来标识实体。 实体的 ID 必须使用 Spring Data 的 @Id 注释进行注释。Spring中文文档

当数据库具有 ID 列的自动递增列时,生成的值在插入数据库后会在实体中设置。Spring中文文档

当实体是新的并且标识符值默认为其初始值时,Spring Data 不会尝试插入标识符列的值。 这适用于基元类型,并且标识符属性使用数字包装类型,例如 。0nullLongSpring中文文档

实体状态检测详细介绍了检测实体是否为新实体或该实体是否预期存在于数据库中的策略。Spring中文文档

一个重要的约束是,在保存实体后,该实体不能再是新的。 请注意,实体是否为新实体是实体状态的一部分。 对于自动递增列,这会自动发生,因为 ID 由 Spring Data 使用 ID 列中的值设置。Spring中文文档

乐观锁定

Spring Data 通过在聚合根上用@Version注释的数值属性来支持乐观锁定。 每当Spring Data保存具有此类版本属性的聚合时,都会发生两件事:Spring中文文档

  • 聚合根目录的 update 语句将包含一个 where 子句,用于检查数据库中存储的版本是否实际未更改。Spring中文文档

  • 如果不是这种情况,则会抛出一个。OptimisticLockingFailureExceptionSpring中文文档

此外,在实体和数据库中,version 属性都会增加,因此并发操作将注意到更改并抛出一个(如果适用),如上所述。OptimisticLockingFailureExceptionSpring中文文档

此过程也适用于插入新聚合,其中 or version 表示新实例,然后增加的实例将该实例标记为不再是新实例,这使得这在对象构造期间生成 id 的情况下(例如使用 UUID 时)非常有效。null0Spring中文文档

在删除期间,版本检查也适用,但不会增加版本。Spring中文文档

@Table
class Person {

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

R2dbcEntityTemplate template = …;

Mono<Person> daenerys = template.insert(new Person("Daenerys"));                      (1)

Person other = template.select(Person.class)
                 .matching(query(where("id").is(daenerys.getId())))
                 .first().block();                                                    (2)

daenerys.setLastname("Targaryen");
template.update(daenerys);                                                            (3)

template.update(other).subscribe(); // emits OptimisticLockingFailureException        (4)
1 最初插入行。 设置为 。version0
2 加载刚刚插入的行。 还是.version0
3 使用 更新行。将 和 bump 设置为 。version = 0lastnameversion1
4 尝试更新之前加载的行,该行仍然具有 .操作失败,并显示 ,因为电流为 。version = 0OptimisticLockingFailureExceptionversion1
1 最初插入行。 设置为 。version0
2 加载刚刚插入的行。 还是.version0
3 使用 更新行。将 和 bump 设置为 。version = 0lastnameversion1
4 尝试更新之前加载的行,该行仍然具有 .操作失败,并显示 ,因为电流为 。version = 0OptimisticLockingFailureExceptionversion1