Persisting Entities
可以使用 CrudRepository.save(…)
方法保存聚合。如果聚合是新的,则会导致聚合根的插入,随后是直接或间接引用实体的插入语句。
Saving an aggregate can be performed with the CrudRepository.save(…)
method.
If the aggregate is new, this results in an insert for the aggregate root, followed by insert statements for all directly or indirectly referenced entities.
如果聚合根不是新的,则所有引用实体都将被删除,聚合根将被更新,并且所有引用实体都将再次插入。请注意,一个实例是否是新的,是该实例状态的一部分。
If the aggregate root is not new, all referenced entities get deleted, the aggregate root gets updated, and all referenced entities get inserted again. Note that whether an instance is new is part of the instance’s state.
这种方法有一些明显的缺点。如果只有几个被引用的实体发生了实际更改,那么删除和插入就是浪费。虽然这个过程可以并且可能得到改善,但 Spring Data JDBC 能够提供的东西有一些限制。它不知道聚合的先前状态。因此,任何更新过程都必须始终获取数据库中找到的任何内容,并确保将其转换为传递给保存方法的实体状态。 |
This approach has some obvious downsides. If only few of the referenced entities have been actually changed, the deletion and insertion is wasteful. While this process could and probably will be improved, there are certain limitations to what Spring Data JDBC can offer. It does not know the previous state of an aggregate. So any update process always has to take whatever it finds in the database and make sure it converts it to whatever is the state of the entity passed to the save method. |
有关更多详细信息,另请参见 Entity State Detection。
See also Entity State Detection for further details.
Loading Aggregates
Spring Data JDBC 提供了两种加载聚合的方式:
Spring Data JDBC offers two ways how it can load aggregates:
-
The traditional and before version 3.2 the only way is really simple: Each query loads the aggregate roots, independently if the query is based on a
CrudRepository
method, a derived query or a annotated query. If the aggregate root references other entities those are loaded with separate statements. -
Spring Data JDBC 3.2 allows the use of Single Query Loading. With this an arbitrary number of aggregates can be fully loaded with a single SQL query. This should be significantly more efficient, especially for complex aggregates, consisting of many entities.[.iokays-translated-cd44d8225dfb62158b8936c524412e11] 目前,单查询加载受到不同方式的限制:
Currently, Single Query Loading is restricted in different ways:
-
The aggregate must not have nested collections, this includes
Map
. The plan is to remove this constraint in the future. -
The aggregate must not use
AggregateReference
or embedded entities. The plan is to remove this constraint in the future. -
The database dialect must support it.Of the dialects provided by Spring Data JDBC all but H2 and HSQL support this. H2 and HSQL don’t support analytic functions (aka windowing functions).
-
It only works for the find methods in
CrudRepository
, not for derived queries and not for annotated queries. The plan is to remove this constraint in the future. -
Single Query Loading needs to be enabled in the
JdbcMappingContext
, by callingsetSingleQueryLoadingEnabled(true)
如果不满足任何条件,Spring Data JDBC 将回退到加载聚合的默认方法。
If any condition is not fulfilled Spring Data JDBC falls back to the default approach of loading aggregates.
单查询加载将被视为实验性功能。我们欢迎你对它的使用方式提供反馈。 |
Single Query Loading is to be considered experimental. We appreciate feedback on how it works for you. |
虽然单查询加载可以缩写为 SQL,但我们强烈建议不要这么做,因为几乎肯定会与结构化查询语言产生混淆。 |
While Single Query Loading can be abbreviated as SQL, but we highly discourage doing so since confusion with Structured Query Language is almost guaranteed. |
ID Generation
ID Generation
Spring Data 使用标识符属性识别实体。实体的 ID 必须使用 Spring Data 的 @Id
注解进行注释。
Spring Data uses the identifer property to identify entities.
The ID of an entity must be annotated with Spring Data’s @Id
annotation.
当你的数据库为 ID 列拥有自动增加列时,在将其插入数据库之后,生成的值会设置在实体中。
When your database has an auto-increment column for the ID column, the generated value gets set in the entity after inserting it into the database.
当实体是新的且标识符值默认为其初始值时,Spring Data 不会尝试插入标识符列的值。对于原始类型是 0
,对于标识符属性使用 Long
等数字包装器类型则为 null
。
Spring Data does not attempt to insert values of identifier columns when the entity is new and the identifier value defaults to its initial value.
That is 0
for primitive types and null
if the identifier property uses a numeric wrapper type such as Long
.
Entity State Detection 详细解释了检测实体是新实体还是它存在于数据库中的策略。
Entity State Detection explains in detail the strategies to detect whether an entity is new or whether it is expected to exist in your database.
一个重要的约束是,在保存一个实体之后,实体不能再是新的。请注意,实体是否为新实体是实体状态的一部分。对于自动增加列,这是自动发生的,因为 ID 由 Spring Data 使用 ID 列中的值进行设置。
One important constraint is that, after saving an entity, the entity must not be new anymore. Note that whether an entity is new is part of the entity’s state. With auto-increment columns, this happens automatically, because the ID gets set by Spring Data with the value from the ID column.
Optimistic Locking
Spring Data 通过聚合根上使用 @Version
注释的数字属性来支持乐观锁定。每当 Spring Data 保存具有此类版本属性的聚合时,都会发生两件事:
Spring Data supports optimistic locking by means of a numeric attribute that is annotated with
@Version
on the aggregate root.
Whenever Spring Data saves an aggregate with such a version attribute two things happen:
-
The update statement for the aggregate root will contain a where clause checking that the version stored in the database is actually unchanged.
-
If this isn’t the case an
OptimisticLockingFailureException
will be thrown.
另外,版本属性在实体和数据库中都会增加,因此,并发操作会注意到更改,并在适用的情况下抛出`OptimisticLockingFailureException` ,如上所述。
Also, the version attribute gets increased both in the entity and in the database so a concurrent action will notice the change and throw an OptimisticLockingFailureException
if applicable as described above.
此过程也适用于插入新聚合,其中 null
或 0
版本指示一个新实例,而增加的实例随后将实例标记为不再是新的,这在对象构造期间生成 id 的情况下可以很好地发挥作用,例如在使用 UUID 时。
This process also applies to inserting new aggregates, where a null
or 0
version indicates a new instance and the increased instance afterwards marks the instance as not new anymore, making this work rather nicely with cases where the id is generated during object construction for example when UUIDs are used.
在删除期间,版本检查也会适用,但不会增加版本。
During deletes the version check also applies but no version is increased.