Persisting Entities
可以使用 CrudRepository.save(…)
方法保存聚合。如果聚合是新的,则会导致聚合根的插入,随后是直接或间接引用实体的插入语句。
如果聚合根不是新的,则所有引用实体都将被删除,聚合根将被更新,并且所有引用实体都将再次插入。请注意,一个实例是否是新的,是该实例状态的一部分。
这种方法有一些明显的缺点。如果只有几个被引用的实体发生了实际更改,那么删除和插入就是浪费。虽然这个过程可以并且可能得到改善,但 Spring Data JDBC 能够提供的东西有一些限制。它不知道聚合的先前状态。因此,任何更新过程都必须始终获取数据库中找到的任何内容,并确保将其转换为传递给保存方法的实体状态。 |
有关更多详细信息,另请参见 Entity State Detection。
Loading Aggregates
Spring Data JDBC 提供了两种加载聚合的方式:
-
传统版本(3.2 之前)的方式非常简单:每次查询都加载聚合根,无论查询是基于一个
CrudRepository
方法、派生查询还是带注释的查询,都如此。如果聚合根引用其他实体,则这些实体会通过单独的语句加载。 -
Spring Data JDBC 3.2 允许使用 Single Query Loading。通过此方法,可以使用一条 SQL 查询完全加载任意数量的聚合。对于包含多个实体的复杂聚合而言,此方法应该明显更高效。目前,单查询加载受到不同方式的限制:
-
聚合不得包含嵌套集合,其中包括
Map
。计划在未来删除此约束。 -
聚合不得使用
AggregateReference
或嵌入式实体。计划在未来删除此约束。 -
数据库方言必须支持它。在 Spring Data JDBC 提供的方言中,除了 H2 和 HSQL 之外,所有其他方言都支持它。H2 和 HSQL 不支持分析函数(又称窗口函数)。
-
它仅适用于
CrudRepository
中的查找方法,不适用于派生查询和带注释的查询。计划在未来删除此约束。 -
需要在
JdbcMappingContext
中通过调用setSingleQueryLoadingEnabled(true)
启用单查询加载。
-
如果不满足任何条件,Spring Data JDBC 将回退到加载聚合的默认方法。
单查询加载将被视为实验性功能。我们欢迎你对它的使用方式提供反馈。 |
虽然单查询加载可以缩写为 SQL,但我们强烈建议不要这么做,因为几乎肯定会与结构化查询语言产生混淆。 |
ID Generation
ID Generation
Spring Data 使用标识符属性识别实体。实体的 ID 必须使用 Spring Data 的 @Id
注解进行注释。
当你的数据库为 ID 列拥有自动增加列时,在将其插入数据库之后,生成的值会设置在实体中。
当实体是新的且标识符值默认为其初始值时,Spring Data 不会尝试插入标识符列的值。对于原始类型是 0
,对于标识符属性使用 Long
等数字包装器类型则为 null
。
Entity State Detection 详细解释了检测实体是新实体还是它存在于数据库中的策略。
一个重要的约束是,在保存一个实体之后,实体不能再是新的。请注意,实体是否为新实体是实体状态的一部分。对于自动增加列,这是自动发生的,因为 ID 由 Spring Data 使用 ID 列中的值进行设置。
Optimistic Locking
Spring Data 通过聚合根上使用 @Version
注释的数字属性来支持乐观锁定。每当 Spring Data 保存具有此类版本属性的聚合时,都会发生两件事:
-
聚合根的更新语句将包含一个 where 子句,检查数据库中存储的版本是否实际上未更改。
-
如果不是这种情况,将抛出
OptimisticLockingFailureException
。
另外,版本属性在实体和数据库中都会增加,因此,并发操作会注意到更改,并在适用的情况下抛出`OptimisticLockingFailureException` ,如上所述。
此过程也适用于插入新聚合,其中 null
或 0
版本指示一个新实例,而增加的实例随后将实例标记为不再是新的,这在对象构造期间生成 id 的情况下可以很好地发挥作用,例如在使用 UUID 时。
在删除期间,版本检查也会适用,但不会增加版本。