原题目:Spring 认证 |Spring Data JDBC- 如何应用自定义 ID 生成
这是对于如何解决应用 Spring Data JDBC 时可能遇到的各种挑战的系列文章的第一篇。
如果你不理解 Spring Data JDBC,你应该首先浏览它的介绍和文章,它解释了 Spring Data JDBC 上下文中的相关性。置信我,这很重要。
文章基于我在 2021 年秋季一期上这篇文章的局部演讲。
应用 ID – 特地是当您想要管制实体的 ID 并且不会抉择什么数据库时,您的抉择是什么。
假如状况下,类型数据列 JDBC 假如的 ID 通过生成 SERIAL 或 AUTOINCREMENT 失去。,聚合根执行插入操作。数据库生成一个 ID,这个 ID 由 Spring Data JDBC 在聚合根中设置。
思考一个由单个简略的类组成的简略聚合:
类小黄人 {
@ID
长 ID;
字符串名称;
Minion(字符串名称){
this.name = 名称;
}
}
进一步思考默认 CrudRepository。
接口 MinionRepository 扩大 CrudRepository {
}
存储库会主动连贯到您的代码中,如下所示:
@主动连线
MinionRepository 随从;
以下工作失常:
Minion before = new Minion(“Bob”);
assertThat(before.id).isNull();
Minion after = minions.save(before);
assertThat(after.id).isNotNull();
然而下一点点:
Minion before = new Minion(“Stuart”);
before.id = 42L;
minions.save(before);
更新语句,Spring Data JDBC 尝试执行更新,因为 ID 曾经设置。然而,因为实际上是新的,更新语句影响零行 Spring Data JDBC 抛出异样。
有几种办法能够解决这个问题。我曾经找到了你不同的解决办法,并且曾经找到了我认为最简略的办法,因而能够找到适宜的办法,你就能够进行浏览。之后回来浏览其余选项并进步您的 Spring Data 技能。
版本
将版本属性增加到您的聚合属性。“版本属性”是指用 @Version。此类的次要目标是能够乐观锁定。然而,作为属性,Spring Data JDBC 应用版本属性来确定聚合根是否是新的。只有版本是 null 或 0 原始类型,聚合就被认为是新的,即便 id 设置了。
应用这种办法,您必须更改实体和(当然)零碎,但别无其他。
此外,对于许多应用程序来说,乐观的最后是很多。
咱们把原来的 Minion 变成了一个 VersionedMinion:
类 VersionedMinion {
@Id 长 ID;
字符串名称;
@Version 整数版本;
VersionedMinion(长 ID,字符串名称){
this.id = id;
this.name = 名称;
}
}
通过此更改,以下结构无效:
VersionedMinion before = new VersionedMinion(23L, “Bob”);
assertThat(before.id).isNotNull();
versionedMinions.save(before);
VersionedMinion 从新加载 = versionedMinions.findById(before.id).get();
assertThat(reloaded.name).isEqualTo(“Bob”);
样板
一种让您的遗赠附带 ID 的办法是本人另外插入物。您能够通过注入 JdbcAggregateTemplate 并调用 JdbcAggregateTemplate.insert(T)。这 JdbAggregateTemplate 是存储库上面的底层,因而您应用存储库用于插入的雷同代码,但您决定何时应用插入:
Minion before = new Minion(“Stuart”);
before.id = 42L;
模板. 插入(之前);
Minion reloaded = minions.findById(42L).get();
assertThat(reloaded.name).isEqualTo(“Stuart”);
请留神,咱们不应用存储库农场应用模板,其中注入了以下内容:
@主动连线
JdbcAggregateTemplate 模板;
事件监听器
模板办法十分实用于您曾经晓得 ID 的状况 – 例如,当您从另一个零碎导入数据并且您想要重用该零碎的 ID 时。
如果您不晓得 ID 并且不想在您的业务代码中蕴含任何 ID 相干的内容,那么应用 ID 可能是更好的抉择。
咱们的目标正确的目标是在某些生命周期事件期间被调用的豆子。它返回批改潜在的聚合根,因而它也实用于不造成实体类。
在指标中,咱们确定有问题的聚合根是否须要新 ID。如果是这样,咱们将应用咱们抉择的算法生成它。
咱们应用另一种变体 Minion
类 StringIdMinion {
@ID
字符串标识;
字符串名称;
StringIdMinion(字符串名称){
this.name = 名称;
}
}
然而,咱们在配置中注册了一个惊人的例子:
@豆角,扁豆
BeforeSaveCallback beforeSaveCallback() {
返回(minion,mutableAggregateChange)-> {
如果(minion.id == null){
minion.id = UUID.randomUUID().toString();
}
返回仆从;
};
}
保留实体的代码当初看起来就像是由数据库生成的:
StringIdMinion before = new StringIdMinion(“Kevin”);
stringions.save(before);
assertThat(before.id).isNotNull();
StringIdMinion reloaded = stringions.findById(before.id).get();
assertThat(reloaded.name).isEqualTo(“Kevin”);
长久的
一个选项是让化根管制是否应该更新或插入。你能够实现长久化的办法(尤其是实现是新的)来实现这一点。您也想应用聚合根进行更新时,这会抓住。在这种状况下,您须要提出更灵便的策略。
咱们须要 Minion 再次调整咱们的:
类 PersistableMinion 实现 Persistable {
@Id 长 ID;
字符串名称;
PersistableMinion(长 ID,字符串名称){
this.id = id;
this.name = 名称;
}
@笼罩
公共长 getId() {
返回标识;
}
@笼罩
公共布尔 isNew() {
// 这个实现必定不适宜生产应用
返回真;
}
}
保留一个的代码 PersistableMinion 看起来是一样的:
PersistableMinion before = new PersistableMinion(23L, “Dave”);
persistableMinions.save(before);
PersistableMinion 从新加载 = persistableMinions.findById(before.id).get();
assertThat(reloaded.name).isEqualTo(“Dave”);
论断
Spring Data JDBC 提供了大量对于如何管制聚合 ID 的选项。尽管我在示例中应用了十分重大的逻辑,但根本没有什么能阻止您实现您所思考的任何逻辑,因为它们都归结为 Java 代码。
残缺的示例代码可在 Spring 中国教育管理中心(Spring 认证)数据示例库拜访!