持久化实体
可以使用 `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 方法,不适用于派生查询和带注释的查询。计划将来取消此限制。
-
需要通过调用 `setSingleQueryLoadingEnabled(true)` 在 `JdbcMappingContext` 中启用单查询加载。
-
如果任何条件不满足,Spring Data JDBC 将回退到加载聚合的默认方法。
单查询加载应被视为实验性功能。我们感谢您提供使用情况反馈。 |
虽然单查询加载可以缩写为 SQL,但我们强烈建议不要这样做,因为与结构化查询语言混淆的可能性几乎是肯定的。 |
ID 生成
Spring Data 使用标识符属性来标识实体。实体的 ID 必须使用 Spring Data 的 @Id
注解进行注解。
当您的数据库的 ID 列具有自动递增列时,在将其插入数据库后,生成的 value 将在实体中设置。
当实体为新实体并且标识符值默认为其初始值时,Spring Data 不会尝试插入标识符列的值。对于基本类型,这是 `0`,如果标识符属性使用数字包装器类型(例如 `Long`),则为 `null`。
实体状态检测 详细解释了检测实体是新的还是预期存在于数据库中的策略。
一个重要的约束是,保存实体后,实体不能再是新的了。请注意,实体是否为新实体是实体状态的一部分。对于自动递增列,这会自动发生,因为 ID 将由 Spring Data 使用 ID 列中的值进行设置。
模板 API
作为 Repository 的替代方案,Spring Data JDBC 提供了 JdbcAggregateTemplate
作为在关系数据库中加载和持久化实体的更直接方法。在很大程度上,Repository 使用 `JdbcAggregateTemplate` 来实现其功能。
本节仅重点介绍 `JdbcAggregateTemplate` 最有趣的部分。有关更完整的概述,请参阅 `JdbcAggregateTemplate` 的 JavaDoc。
访问 JdbcAggregateTemplate
JdbcAggregateTemplate
旨在用作 Spring bean。如果您已将应用程序设置为包含 Spring Data JDBC,则可以在任何 Spring bean 中配置对 JdbcAggregateTemplate
的依赖项,Spring 框架会注入已正确配置的实例。
这包括您用于实现 Spring Data 存储库自定义方法的片段,让您可以使用 JdbcAggregateTemplate
来自定义和扩展您的存储库。
持久化
JdbcAggregateTemplate
提供三种持久化实体的方法:save
、insert
和 update
。每种方法都有两种形式:一种操作单个聚合,名称与上述名称完全相同;另一种以 All
为后缀,操作 Iterable
。
save
的作用与存储库中同名方法的作用相同。
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 保存具有此类版本属性的聚合时,会发生两件事:
-
聚合根的更新语句将包含一个 where 子句,用于检查数据库中存储的版本是否实际上未更改。
-
如果不是这种情况,将抛出
OptimisticLockingFailureException
。
此外,版本属性在实体和数据库中都会增加,因此并发操作将注意到更改,并在适用时抛出 OptimisticLockingFailureException
,如上所述。
此过程也适用于插入新的聚合,其中 null
或 0
版本表示新实例,而增加后的实例随后将实例标记为不再是新的,这与例如使用 UUID 时在对象构造期间生成 id 的情况非常匹配。
在删除过程中,版本检查也适用,但版本不会增加。