原標(biāo)題:Spring認(rèn)證|Spring Data JDBC-如何使用自定義ID生成
這是關(guān)于如何解決使用 Spring Data JDBC 時(shí)可能遇到的各種挑戰(zhàn)的系列文章的第一篇。
如果你不了解 Spring Data JDBC,你應(yīng)該首先閱讀它的介紹和文章,它解釋了 Spring Data JDBC 上下文中的相關(guān)性。相信我,這很重要。
文章基于我在 2021 年春季一期上這篇文章的部分演講。
使用 ID - 特別是當(dāng)您想要控制實(shí)體的 ID 并且不會(huì)選擇什么數(shù)據(jù)庫(kù)時(shí),您的選擇是什么。
假設(shè)情況下,類型數(shù)據(jù)列JDBC假設(shè)的ID通過(guò)生成SERIAL或AUTOINCREMENT得到。 ,聚合根執(zhí)行插入操作。數(shù)據(jù)庫(kù)生成一個(gè)ID,這個(gè)ID由Spring Data JDBC在聚合根中設(shè)置。
考慮一個(gè)由單個(gè)簡(jiǎn)單的類組成的簡(jiǎn)單聚合:
類小黃人{(lán)
@ID
長(zhǎng)ID;
字符串名稱;
Minion(字符串名稱){
this.name = 名稱;
}
}
進(jìn)一步考慮默認(rèn)CrudRepository。
接口 MinionRepository 擴(kuò)展 CrudRepository {
}
存儲(chǔ)庫(kù)會(huì)自動(dòng)連接到您的代碼中,如下所示:
@自動(dòng)連線
MinionRepository 隨從;
以下工作正常:
Minion before = new Minion("Bob");
assertThat(before.id).isNull();
Minion after = minions.save(before);
assertThat(after.id).isNotNull();
但是下一點(diǎn)點(diǎn):
Minion before = new Minion("Stuart");
before.id = 42L;
minions.save(before);
更新語(yǔ)句,Spring Data JDBC 嘗試執(zhí)行更新,因?yàn)?ID 已經(jīng)設(shè)置。但是,因?yàn)閷?shí)際上是新的,更新語(yǔ)句影響零行 Spring Data JDBC 拋出異常。
有幾種方法可以解決這個(gè)問題。我已經(jīng)找到了你不同的解決方法,并且已經(jīng)找到了我認(rèn)為最簡(jiǎn)單的方法,因此可以找到適合的方法,你就可以停止閱讀。之后回來(lái)閱讀其他選項(xiàng)并提高您的 Spring Data 技能。
版本
將版本屬性添加到您的聚合屬性?!鞍姹緦傩浴笔侵赣聾Version。此類的主要目的是可以樂觀鎖定。但是,作為屬性,Spring Data JDBC 使用版本屬性來(lái)確定聚合根是否是新的。 只要版本是null 或0 原始類型,聚合就被認(rèn)為是新的,即使id設(shè)置了。
使用這種方法,您必須更改實(shí)體和(當(dāng)然)系統(tǒng),但別無(wú)其他。
此外,對(duì)于許多應(yīng)用程序來(lái)說(shuō),樂觀的最初是很多。
我們把原來(lái)的Minion變成了一個(gè)VersionedMinion:
類 VersionedMinion {
@Id 長(zhǎng) ID;
字符串名稱;
@Version 整數(shù)版本;
VersionedMinion(長(zhǎng)ID,字符串名稱){
this.id = id;
this.name = 名稱;
}
}
通過(guò)此更改,以下構(gòu)造有效:
VersionedMinion before = new VersionedMinion(23L, "Bob");
assertThat(before.id).isNotNull();
versionedMinions.save(before);
VersionedMinion 重新加載 = versionedMinions.findById(before.id).get();
assertThat(reloaded.name).isEqualTo("Bob");
樣板
一種讓您的遺贈(zèng)附帶 ID 的方法是自己另外插入物。您可以通過(guò)注入 JdbcAggregateTemplate 并調(diào)用 JdbcAggregateTemplate.insert(T)。這JdbAggregateTemplate是存儲(chǔ)庫(kù)下面的底層,因此您使用存儲(chǔ)庫(kù)用于插入的相同代碼,但您決定何時(shí)使用插入:
Minion before = new Minion("Stuart");
before.id = 42L;
模板.插入(之前);
Minion reloaded = minions.findById(42L).get();
assertThat(reloaded.name).isEqualTo("Stuart");
請(qǐng)注意,我們不使用存儲(chǔ)庫(kù)農(nóng)場(chǎng)使用模板,其中注入了以下內(nèi)容:
@自動(dòng)連線
JdbcAggregateTemplate 模板;
事件監(jiān)聽器
模板方法非常適用于您已經(jīng)知道 ID 的情況 - 例如,當(dāng)您從另一個(gè)系統(tǒng)導(dǎo)入數(shù)據(jù)并且您想要重用該系統(tǒng)的 ID 時(shí)。
如果您不知道 ID 并且不想在您的業(yè)務(wù)代碼中包含任何 ID 相關(guān)的內(nèi)容,那么使用 ID 可能是更好的選擇。
我們的目的正確的目的是在某些生命周期事件期間被調(diào)用的豆子。它返回修改潛在的聚合根,因此它也適用于不形成實(shí)體類。
在目標(biāo)中,我們確定有問題的聚合根是否需要新 ID。 如果是這樣,我們將使用我們選擇的算法生成它。
我們使用另一種變體 Minion
類 StringIdMinion {
@ID
字符串標(biāo)識(shí);
字符串名稱;
StringIdMinion(字符串名稱){
this.name = 名稱;
}
}
但是,我們?cè)谂渲弥凶?cè)了一個(gè)驚人的例子:
@豆角,扁豆
BeforeSaveCallback beforeSaveCallback() {
返回(minion,mutableAggregateChange)-> {
如果(minion.id == null){
minion.id = UUID.randomUUID().toString();
}
返回仆從;
};
}
保存實(shí)體的代碼現(xiàn)在看起來(lái)就像是由數(shù)據(jù)庫(kù)生成的:
StringIdMinion before = new StringIdMinion("Kevin");
stringions.save(before);
assertThat(before.id).isNotNull();
StringIdMinion reloaded = stringions.findById(before.id).get();
assertThat(reloaded.name).isEqualTo("Kevin");
持久的
一個(gè)選項(xiàng)是讓化根控制是否應(yīng)該更新或插入。你可以實(shí)現(xiàn)持久化的方法(尤其是實(shí)現(xiàn)是新的)來(lái)實(shí)現(xiàn)這一點(diǎn)。您也想使用聚合根進(jìn)行更新時(shí),這會(huì)抓住。在這種情況下,您需要提出更靈活的策略。
我們需要 Minion 再次調(diào)整我們的:
類 PersistableMinion 實(shí)現(xiàn) Persistable {
@Id 長(zhǎng) ID;
字符串名稱;
PersistableMinion(長(zhǎng)ID,字符串名稱){
this.id = id;
this.name = 名稱;
}
@覆蓋
公共長(zhǎng) getId() {
返回標(biāo)識(shí);
}
@覆蓋
公共布爾 isNew() {
// 這個(gè)實(shí)現(xiàn)肯定不適合生產(chǎn)使用
返回真;
}
}
保存一個(gè)的代碼 PersistableMinion 看起來(lái)是一樣的:
PersistableMinion before = new PersistableMinion(23L, "Dave");
persistableMinions.save(before);
PersistableMinion 重新加載 = persistableMinions.findById(before.id).get();
assertThat(reloaded.name).isEqualTo("Dave");
結(jié)論
Spring Data JDBC 提供了大量關(guān)于如何控制聚合 ID 的選項(xiàng)。雖然我在示例中使用了非常嚴(yán)重的邏輯,但基本沒有什么能阻止您實(shí)現(xiàn)您所考慮的任何邏輯,因?yàn)樗鼈兌細(xì)w結(jié)為 Java 代碼。
完整的示例代碼可在Spring中國(guó)教育管理中心(Spring認(rèn)證)數(shù)據(jù)示例庫(kù)訪問!
-
spring
+關(guān)注
關(guān)注
0文章
340瀏覽量
14344 -
JDBC
+關(guān)注
關(guān)注
0文章
25瀏覽量
13408
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論