|
此版本仍在开发中,尚未被视为稳定版本。对于最新的稳定版本,请使用 Spring Data Relational 3.4.0! |
持久化实体
R2dbcEntityTemplate是 Spring Data R2DBC 的中心入口点。
它为典型的临时用例(如查询、插入、更新和删除数据)提供直接的面向实体的方法和更窄、更流畅的界面。
入口点 (insert(),select(),update()等)遵循基于要运行的作的自然命名架构。
从入口点开始,API 旨在仅提供依赖于上下文的方法,这些方法会导致创建和运行 SQL 语句的终止方法。
Spring Data R2DBC 使用R2dbcDialect抽象来确定绑定标记、分页支持和底层驱动程序原生支持的数据类型。
所有终端方法始终返回一个Publishertype 来表示所需的作。
实际的对账单在订阅时发送到数据库。 |
插入和更新实体的方法
有几种方便的方法R2dbcEntityTemplate用于保存和插入对象。
为了对转换过程进行更精细的控制,你可以用 Spring 转换器注册R2dbcCustomConversions— 例如Converter<Person, OutboundRow>和Converter<Row, Person>.
使用 save作的简单情况是保存一个 POJO。 在这种情况下,table name 由类的名称(非完全限定)确定。 您还可以使用特定的集合名称调用 save作。 您可以使用映射元数据覆盖用于存储对象的集合。
插入或保存时,如果Id属性,则假设其值将由数据库自动生成。
因此,为了自动生成Idproperty 或 field 必须是Long或Integer.
下面的示例演示如何插入行并检索其内容:
R2dbcEntityTemplatePerson person = new Person("John", "Doe");
Mono<Person> saved = template.insert(person);
Mono<Person> loaded = template.selectOne(query(where("firstname").is("John")),
Person.class);
可以使用以下 insert 和 update作:
也可以使用一组类似的 insert作:
-
Mono<T>插入(T objectToSave):将对象插入到默认表中。 -
Mono<T>更新(T objectToSave):将对象插入到默认表中。
可以使用 Fluent API 自定义表名称。
选择数据
这select(…)和selectOne(…)方法R2dbcEntityTemplate用于从表中选择数据。
这两种方法都采用Query对象,则WHERE子句中,ORDER BY子句和 limit/offset 分页。
限制/偏移功能对应用程序是透明的,无论底层数据库如何。
此功能由R2dbcDialect抽象化来满足各个 SQL 风格之间的差异。
R2dbcEntityTemplateFlux<Person> loaded = template.select(query(where("firstname").is("John")),
Person.class);
Fluent API
本节介绍 Fluent API 的用法。 请考虑以下简单查询:
Flux<Person> people = template.select(Person.class) (1)
.all(); (2)
| 1 | 用Person使用select(…)方法映射 上的表格结果Personresult 对象。 |
| 2 | 取all()rows 返回一个Flux<Person>而不会限制结果。 |
以下示例声明了一个更复杂的查询,该查询按名称指定表名,即WHEREcondition 和ORDER BY第:
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 | 发出的查询声明了WHEREcondition 开启firstname和lastname列来筛选结果。 |
| 3 | 结果可以按单个列名称排序,从而生成ORDER BY第。 |
| 4 | 选择一个结果仅获取一行。
这种使用行的方式期望查询只返回一个结果。Mono发出一个IncorrectResultSizeDataAccessException如果查询生成多个结果。 |
您可以通过 Projections 直接将 Projections 应用于结果,方法是通过select(Class<?>). |
您可以通过以下终止方法在检索单个实体和检索多个实体之间切换:
-
first():仅使用第一行,返回Mono. 返回的Mono如果查询未返回任何结果,则完成而不发出对象。 -
one():只使用一行,返回一个Mono. 返回的Mono如果查询未返回任何结果,则完成而不发出对象。 如果查询返回多行,则Mono完成异常发射IncorrectResultSizeDataAccessException. -
all():使用返回Flux. -
count():应用返回的计数投影Mono<Long>. -
exists():通过返回Mono<Boolean>.
您可以使用select()入口点来表达你的SELECT查询。
结果SELECT查询支持常用子句 (WHERE和ORDER BY) 并支持分页。
Fluent API 样式允许您将多个方法链接在一起,同时拥有易于理解的代码。
为了提高可读性,你可以使用静态导入,这样你就可以避免使用 'new' 关键字来创建Criteria实例。
Criteria 类的方法
这Criteriaclass 提供了以下方法,所有这些方法都对应于 SQL 运算符:
-
Criteria和(String column):添加已链接的Criteria替换为指定的property到当前Criteria并返回新创建的 ID。 -
Criteria或(String column):添加已链接的Criteria替换为指定的property到当前Criteria并返回新创建的 ID。 -
Criteria大于(Object o):使用运算符创建标准。> -
CriteriagreaterThanOrEquals(Object o):使用运算符创建标准。>= -
Criteria在(Object… o):使用IN运算符。 -
Criteria在(Collection<?> collection):使用IN运算符。 -
Criteria是(Object o):使用列匹配 (property = value). -
CriteriaisNull :使用()IS NULL算子。 -
CriteriaisNotNull :使用()IS NOT NULL算子。 -
Criteria小于(Object o):使用运算符创建标准。< -
CriterialessThanOrEquals(Object o):使用⇐算子。 -
Criteria喜欢(Object o):使用LIKE运算符。 -
Criteria不(Object o):使用!=算子。 -
CriterianotIn(Object… o):使用NOT IN运算符。 -
CriterianotIn(Collection<?> collection):使用NOT IN运算符。 您可以使用Criteria跟SELECT,UPDATE和DELETE查询。
插入数据
您可以使用insert()插入数据的入口点。
请考虑以下简单的类型化插入作:
Mono<Person> insert = template.insert(Person.class) (1)
.using(new Person("John", "Doe")); (2)
| 1 | 用Person使用into(…)method 设置INTO表,基于映射元数据。
它还准备 insert 语句以接受Person对象进行插入。 |
| 2 | 提供标量Person对象。
或者,您也可以提供Publisher要运行INSERT语句。
此方法提取所有非null值并插入它们。 |
更新数据
您可以使用update()用于更新行的入口点。
更新数据首先通过接受指定要更新的表Update指定 assignments。
它还接受Query要创建一个WHERE第。
请考虑以下简单的类型化 update作:
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 | 应用Update对象。
在这种情况下设置age自42并返回受影响的行数。 |
删除数据
您可以使用delete()删除行的入口点。
删除数据从要从中删除的表的规范开始,并且(可选)接受Criteria要创建一个WHERE第。
请考虑以下简单的 insert作:
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 | 应用 delete作并返回受影响的行数。 |
使用 Repositories,可以使用ReactiveCrudRepository.save(…)方法。
如果实体是新实体,则会导致实体的插入。
如果实体不是新实体,则会更新该实体。 请注意,实例是否为新实例是实例状态的一部分。
| 这种方法有一些明显的缺点。 如果实际只更改了很少的引用实体,则删除和插入是浪费。 虽然这个过程可以而且可能会得到改进,但 Spring Data R2DBC 可以提供的功能存在一定的限制。 它不知道聚合的先前状态。 因此,任何更新过程都必须获取它在数据库中找到的任何内容,并确保将其转换为传递给 save 方法的实体的状态。 |
ID 生成
Spring Data 使用 identifer 属性来标识实体。
实体的 ID 必须使用 Spring Data 的@Id注解。
当您的数据库具有 ID 列的自动增量列时,在将生成的值插入数据库后,将在实体中设置该值。
当实体是新实体并且标识符值默认为其初始值时,Spring Data 不会尝试插入标识符列的值。
那是0对于 primitive types和null如果 identifier 属性使用数字包装器类型,例如Long.
实体状态检测 详细介绍了检测实体是新实体还是应存在于数据库中的策略。
一个重要的约束是,在保存实体后,该实体不能再是新的。 请注意,实体是否为 new 是实体状态的一部分。 对于自增列,这会自动发生,因为 ID 是由 Spring Data 使用 ID 列中的值设置的。
乐观锁定
Spring Data 通过一个数字属性来支持乐观锁定,该属性被注释有@Version在聚合根上。
每当 Spring Data 保存具有此类 version 属性的聚合时,都会发生两种情况:
-
聚合根的 update 语句将包含一个 where 子句,用于检查存储在数据库中的版本是否实际保持不变。
-
如果不是这种情况,则
OptimisticLockingFailureException将被抛出。
此外,version 属性在实体和数据库中都会增加,因此并发作会注意到更改并抛出OptimisticLockingFailureException如果适用,如上所述。
此过程也适用于插入新聚合,其中null或0version 表示新实例,随后增加的实例会将该实例标记为不再是新的,这使得这在对象构建期间生成 id 的情况下(例如使用 UUID 时)非常有效。
在删除期间,版本检查也适用,但不会增加版本。
@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 | 最初插入行。version设置为0. |
| 2 | 加载刚刚插入的行。version仍然0. |
| 3 | 使用 更新version = 0.将lastname和 bumpversion自1. |
| 4 | 尝试更新之前加载的行,该行仍然具有version = 0.作失败,并显示OptimisticLockingFailureException,作为当前version是1. |