Persisting Entities

可以使用 CrudRepository.save(…) 方法保存聚合。如果聚合是新的,则会导致聚合根的插入,随后是直接或间接引用实体的插入语句。 如果聚合根不是新的,则所有引用实体都将被删除,聚合根将被更新,并且所有引用实体都将再次插入。请注意,一个实例是否是新的,是该实例状态的一部分。

这种方法有一些明显的缺点。如果只有几个被引用的实体发生了实际更改,那么删除和插入就是浪费。虽然这个过程可以并且可能得到改善,但 Spring Data JDBC 能够提供的东西有一些限制。它不知道聚合的先前状态。因此,任何更新过程都必须始终获取数据库中找到的任何内容,并确保将其转换为传递给保存方法的实体状态。

有关更多详细信息,另请参见 Entity State Detection

Loading Aggregates

Spring Data JDBC 提供了两种加载聚合的方式:

  1. 传统版本(3.2 之前)的方式非常简单:每次查询都加载聚合根,无论查询是基于一个 CrudRepository 方法、派生查询还是带注释的查询,都如此。如果聚合根引用其他实体,则这些实体会通过单独的语句加载。

  2. Spring Data JDBC 3.2 允许使用 Single Query Loading。通过此方法,可以使用一条 SQL 查询完全加载任意数量的聚合。对于包含多个实体的复杂聚合而言,此方法应该明显更高效。目前,单查询加载受到不同方式的限制:

    1. 聚合不得包含嵌套集合,其中包括 Map。计划在未来删除此约束。

    2. 聚合不得使用 AggregateReference 或嵌入式实体。计划在未来删除此约束。

    3. 数据库方言必须支持它。在 Spring Data JDBC 提供的方言中,除了 H2 和 HSQL 之外,所有其他方言都支持它。H2 和 HSQL 不支持分析函数(又称窗口函数)。

    4. 它仅适用于 CrudRepository 中的查找方法,不适用于派生查询和带注释的查询。计划在未来删除此约束。

    5. 需要在 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` ,如上所述。

此过程也适用于插入新聚合,其中 null0 版本指示一个新实例,而增加的实例随后将实例标记为不再是新的,这在对象构造期间生成 id 的情况下可以很好地发挥作用,例如在使用 UUID 时。

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