原標題: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認證)數據示例庫訪問!
-
spring
+關注
關注
0文章
340瀏覽量
14390 -
JDBC
+關注
關注
0文章
25瀏覽量
13430
發布評論請先 登錄
相關推薦
評論