持久化实体
可以使用 CrudRepository.save(…)
方法保存聚合。如果聚合是新的,这将导致聚合根进行插入,然后对所有直接或间接引用的实体进行插入。
如果聚合根不是新的,所有引用的实体将被删除,聚合根将被更新,然后所有引用的实体将再次插入。请注意,实例是否为新实例是实例状态的一部分。
这种方法有一些明显的缺点。如果只有少数引用的实体实际发生了变化,删除和插入就会造成浪费。虽然这个过程可以且可能会得到改进,但 Spring Data JDBC 能提供的能力存在某些限制。它不知道聚合的先前状态。因此,任何更新过程都必须总是获取数据库中找到的任何内容,并确保将其转换为传递给 save 方法的实体的当前状态。 |
另请参阅实体状态检测以获取更多详细信息。
加载聚合
Spring Data JDBC 提供了两种加载聚合的方式
-
传统方式,也是 3.2 版本之前唯一的方式,非常简单:每次查询都加载聚合根,无论查询是基于
CrudRepository
方法、派生查询还是注解查询。如果聚合根引用了其他实体,则这些实体会通过单独的语句加载。 -
Spring Data JDBC 3.2 允许使用 *单查询加载*。通过这种方式,可以使用单个 SQL 查询完整加载任意数量的聚合。这应该会显著提高效率,特别是对于包含许多实体的复杂聚合。
目前,单查询加载在不同方面受到限制
-
聚合不得包含嵌套集合,包括
Map
。计划将来移除此限制。 -
聚合不得使用
AggregateReference
或嵌入式实体。计划将来移除此限制。 -
数据库方言必须支持它。Spring Data JDBC 提供的所有方言中,除了 H2 和 HSQL 外,都支持此功能。H2 和 HSQL 不支持分析函数(也称为窗口函数)。
-
它只适用于
CrudRepository
中的 find 方法,不适用于派生查询和注解查询。计划将来移除此限制。 -
需要在
JdbcMappingContext
中通过调用setSingleQueryLoadingEnabled(true)
来启用单查询加载。
-
如果任何条件不满足,Spring Data JDBC 将回退到默认的加载聚合方式。
单查询加载目前被认为是实验性的。我们非常感谢您提供关于其使用情况的反馈。 |
虽然单查询加载可以缩写为 SQL,但我们强烈不建议这样做,因为这几乎肯定会与结构化查询语言造成混淆。 |
ID 生成
Spring Data 使用标识符属性来识别实体。实体的 ID 必须使用 Spring Data 的 @Id
注解进行标注。
当您的数据库中 ID 列使用了自增(auto-increment)时,生成的值将在实体插入数据库后设置到实体中。
当实体是新的且标识符值默认为其初始值时,Spring Data 不会尝试插入标识符列的值。对于原始类型,初始值是 0
;如果标识符属性使用 Long
等数字包装类型,则初始值是 null
。
实体状态检测详细解释了如何检测实体是否是新的,或者是否预期已存在于数据库中。
一个重要的约束是,实体保存后,它就不再是新的了。请注意,实体是否为新实体是实体状态的一部分。对于自增列,这是自动发生的,因为 Spring Data 会将 ID 列的值设置到实体中。
Template API
作为 Repository 的替代方案,Spring Data JDBC 提供了 JdbcAggregateTemplate
,这是一种在关系型数据库中加载和持久化实体的更直接的方式。Repository 在很大程度上使用 JdbcAggregateTemplate
来实现其功能。
本节仅重点介绍 JdbcAggregateTemplate
中最有趣的部分。有关更完整的概述,请参阅 JdbcAggregateTemplate
的 JavaDoc。
访问 JdbcAggregateTemplate
JdbcAggregateTemplate
旨在用作 Spring bean。如果您已设置好应用程序以包含 Spring Data JDBC,则可以在任何 Spring bean 中配置对 JdbcAggregateTemplate
的依赖,Spring Framework 将注入一个配置正确的实例。
这包括您用于为 Spring Data Repositories 实现自定义方法的部分,让您可以使用 JdbcAggregateTemplate
来自定义和扩展您的 Repositories。
持久化
JdbcAggregateTemplate
提供了三种用于持久化实体的方法:save
、insert
和 update
。每种方法都有两种形式:一种操作单个聚合(名称如上所述),另一种则带有 All
后缀,操作一个 Iterable
。
save
方法与 Repository 中同名方法的功能相同。
insert
和 update
方法会跳过实体是否为新的检查,并根据其名称分别假定聚合是新的或已存在的。
查询
JdbcAggregateTemplate
提供了大量的查询聚合以及聚合集合的方法。其中有一种方法需要特别注意,那就是接受 Query
作为参数的方法。它们允许执行以编程方式构造的查询,如下所示:
template.findOne(query(where("name").is("Gandalf")), Person.class);
query
方法返回的 Query
定义了要选择的列列表、where 子句(通过 CriteriaDefinition),以及 limit 和 offset 子句的规范。有关 Query
类的详细信息,请参阅其 JavaDoc。
Criteria
类(其中 where
是一个静态成员)提供了 org.springframework.data.relational.core.query.CriteriaDefinition[]
的实现,它们代表了查询的 where 子句。
Criteria 类的方法
Criteria
类提供了以下方法,所有这些方法都对应于 SQL 运算符
-
Criteria
and(String column)
: 将一个带指定property
的链式Criteria
添加到当前的Criteria
中,并返回新创建的Criteria
。 -
Criteria
or(String column)
: 将一个带指定property
的链式Criteria
添加到当前的Criteria
中,并返回新创建的Criteria
。 -
Criteria
greaterThan(Object o)
: 使用>
运算符创建一个条件。 -
Criteria
greaterThanOrEquals(Object o)
: 使用>=
运算符创建一个条件。 -
Criteria
in(Object… o)
: 使用IN
运算符为可变参数创建一个条件。 -
Criteria
in(Collection<?> collection)
: 使用IN
运算符为集合创建一个条件。 -
Criteria
is(Object o)
: 使用列匹配(property = value
)创建一个条件。 -
Criteria
isNull()
: 使用IS NULL
运算符创建一个条件。 -
Criteria
isNotNull()
: 使用IS NOT NULL
运算符创建一个条件。 -
Criteria
lessThan(Object o)
: 使用<
运算符创建一个条件。 -
Criteria
lessThanOrEquals(Object o)
: 使用⇐
运算符创建一个条件。 -
Criteria
like(Object o)
: 使用LIKE
运算符创建一个条件,不进行转义字符处理。 -
Criteria
not(Object o)
: 使用!=
运算符创建一个条件。 -
Criteria
notIn(Object… o)
: 使用NOT IN
运算符为可变参数创建一个条件。 -
Criteria
notIn(Collection<?> collection)
: 使用NOT IN
运算符为集合创建一个条件。
乐观锁
Spring Data 通过在聚合根上使用标注了 @Version
注解的数字属性来支持乐观锁。每当 Spring Data 保存带有此版本属性的聚合时,会发生两件事:
-
聚合根的 update 语句将包含一个 where 子句,用于检查数据库中存储的版本是否确实未改变。
-
如果不是这种情况,将抛出
OptimisticLockingFailureException
。
此外,版本属性在实体和数据库中都会增加,以便并发操作会注意到此变化,并在适用时抛出 OptimisticLockingFailureException
,如上所述。
这个过程也适用于插入新的聚合,其中 null
或 0
版本表示新实例,之后增加的版本会将实例标记为不再是新的,这使得它非常适合那些在对象构造期间生成 ID 的情况,例如使用 UUID 时。
在删除过程中,版本检查也适用,但版本不会增加。