麻豆小视频在线观看_中文黄色一级片_久久久成人精品_成片免费观看视频大全_午夜精品久久久久久久99热浪潮_成人一区二区三区四区

首頁 > 開發(fā) > Java > 正文

使用Spring Data JDBC實現(xiàn)DDD聚合的示例代碼

2024-07-14 08:42:27
字體:
供稿:網(wǎng)友

本文討論了Spring Data JDBC如何實現(xiàn)DDD中聚合根存儲的設(shè)計思路,其中主要討論了是不是每個實體都需要一個對應(yīng)數(shù)據(jù)表,這種問題需要根據(jù)具體情況而定。

Spring Data JDBC比JPA更容易理解,比如對象引用特性會很有趣。作為第一個示例,請考慮以下領(lǐng)域模型:

class PurchaseOrder { private @Id Long id; private String shippingAddress; private Set<OrderItem> items = new HashSet<>(); void addItem(int quantity, String product) {  items.add(createOrderItem(quantity, product)); } private OrderItem createOrderItem(int quantity, String product) {  OrderItem item = new OrderItem();  item.product = product;  item.quantity = quantity;  return item; }}class OrderItem { int quantity; String product;}

另外,考慮如下定義的存儲庫:

interface OrderRepository extends CrudRepository<PurchaseOrder, Long> { @Query("select count(*) from order_item") int countItems();}

如果使用商品創(chuàng)建訂單,希望所有商品都能保存:

@Autowired OrderRepository repository;@Testpublic void createUpdateDeleteOrder() { PurchaseOrder order = new PurchaseOrder(); order.addItem(4, "Captain Future Comet Lego set"); order.addItem(2, "Cute blue angler fish plush toy"); PurchaseOrder saved = repository.save(order); assertThat(repository.count()).isEqualTo(1); assertThat(repository.countItems()).isEqualTo(2); …

此外,如果刪除PurchaseOrder,它的所有項目也應(yīng)該被刪除。

 … repository.delete(saved); assertThat(repository.count()).isEqualTo(0); assertThat(repository.countItems()).isEqualTo(0);}

如果我們需要一個語法上相同但語義上不同的關(guān)系呢?上述訂單中包含訂單條目OrderItem , 當訂單刪除時,包含的OrderItem 都刪除了,但是看看看看下面案例,也是使用包含一個集合:

class Book { // … Set<Author> authors = new HashSet<>();}

當書籍絕版時,將Book刪除。所有的作者Author也都丟失了。這當然不是你想要的,因為一些作者可能也寫過其他書。

怎么辦?

讓我們看一看存儲庫實際存在的內(nèi)容。這與一遍又一遍的問題密切相關(guān):是否應(yīng)該在JPA中為每個表創(chuàng)建一個存儲庫?

而正確和權(quán)威的答案是“不”。存儲庫持久聚合并加載聚合。聚合是一個包含各種對象的群,它應(yīng)始終保持一致。此外,它應(yīng)始終保持(和加載)在一起。它有一個對象,稱為聚合根,它是唯一允許外部訪問或引用聚合內(nèi)部的代理或管理者。聚合根是傳遞給存儲庫的,以便持久化聚合里面的對象群。

這提出了一個問題:Spring Data JDBC如何確定什么是聚合的一部分,哪些不是?答案非常簡單:非瞬態(tài)non-transient 引用都是聚合的一部分,這樣就可從聚合根到達聚合內(nèi)部所有內(nèi)容。

OrderItem實例是聚合的一部分,因此被刪除; Author正好相反,實例不是Book聚合的一部分,因此不應(yīng)刪除。所以不應(yīng)該從Book內(nèi)部去引用那些作者Author對象。

問題解決了。好吧,......不是真的。我們?nèi)匀恍枰鎯驮L問有關(guān)Book和Author之間的關(guān)系信息。答案可以在領(lǐng)域驅(qū)動設(shè)計(DDD)中找到,它建議使用ID而不是直接引用。這適用于各種多對X關(guān)系。

如果多個聚合引用同一個實體,則該實體不能成為引用它的多個聚合的一部分,因為它只能是其中一個聚合的一部分。因此,任何“多對一”和“多對多”關(guān)系都只能通過引用id來建模實現(xiàn)了。

這樣可以實現(xiàn)多種目的:

1. 清楚地表示了聚合的邊界。

2. 還完全解耦(至少在應(yīng)用程序的領(lǐng)域模型中)所涉及的兩個聚合。

3. 這種分離可以用不同的方式在數(shù)據(jù)庫中表示:

a. 以通常的方式保留數(shù)據(jù)庫,包括所有外鍵。這意味著必須確保以正確的順序創(chuàng)建和保留聚合。

b. 使用延遲約束,僅在事務(wù)的提交階段進行檢查。這可能會提高吞吐量。它還編纂了最終一致性的版本,其中“最終”與交易結(jié)束相關(guān)聯(lián)。這也允許引用從未存在的聚合,只要它僅在事務(wù)期間發(fā)生。這對于避免大量基礎(chǔ)結(jié)構(gòu)代碼只是為了滿足外鍵和非空約束可能是有用的。

c. 完全刪除外鍵,實現(xiàn)真正的最終一致性。

d. 將引用的聚合保留在不同的數(shù)據(jù)庫中,甚至可能是No SQL存儲。

無論如何,即使Spring Data JDBC也鼓勵應(yīng)用模塊化。此外,如果嘗試遷移一個具有10年歷史的單體,你就會明白它的價值。

使用Spring Data JDBC,您可以建模多對多關(guān)系,如下所示:

class Book { private @Id Long id; private String title; private Set<AuthorRef> authors = new HashSet<>(); public void addAuthor(Author author) {  authors.add(createAuthorRef(author)); } private AuthorRef createAuthorRef(Author author) {  Assert.notNull(author, "Author must not be null");  Assert.notNull(author.id, "Author id, must not be null");  AuthorRef authorRef = new AuthorRef();  authorRef.authorId = author.id;  return authorRef; }}@Table("Book_Author")class AuthorRef { Long authorId ;}class Author { @Id Long id; String name;}

請注意額外的類:AuthorRef,它表示有關(guān)某個作者的Book聚合的知識。它可能包含有關(guān)作者的其他聚合信息,然后實際上會在數(shù)據(jù)庫中重復(fù)??紤]到Author數(shù)據(jù)庫可能與Book數(shù)據(jù)庫完全不同,這會產(chǎn)生很多問題。

另請注意,authors是Book 私有字段,AuthorRef實例化在私有方法createAuthorRef中發(fā)生。因此聚合之外的任何內(nèi)容都不能直接訪問它。Spring Data JDBC絕不需要這樣做,但DDD鼓勵這么做。

下面是測試:

@Testpublic void booksAndAuthors() { Author author = new Author(); author.name = "Greg L. Turnquist"; author = authors.save(author); Book book = new Book(); book.title = "Spring Boot"; book.addAuthor(author); books.save(book); books.deleteAll(); assertThat(authors.count()).isEqualTo(1);}

上述完成了我們設(shè)想功能:刪除書籍后,并沒有將書籍作者數(shù)據(jù)表數(shù)據(jù)全部刪除,雖然作者是書籍的一個私有字段。

總結(jié)一下:

Spring Data JDBC不支持多對一或多對多關(guān)系。要對這些進行建模,請使用ID。

這鼓勵了領(lǐng)域模型的清晰模塊化。

通過類似的思路,避免雙向依賴。聚合內(nèi)部的引用是從聚合根到元素。聚合之間的引用使用只在一個關(guān)聯(lián)方向上使用ID表示。此外,如果需要反向?qū)Ш?,請在存儲庫中使用查詢方法。這樣能清楚確定哪個聚合負責維護引用。

banq注:是不是每個實體都需要一個對應(yīng)數(shù)據(jù)表?根據(jù)具體情況,Order和OrderItem之間生命周期是一致的,刪除訂單,訂單條目也沒有存在意義;而Book和Author則不是生命周期一致的,Book可能是當前有界上下文的聚合根,而Author是另外一個有界上下文如作者管理系統(tǒng)的聚合根,如果刪除Book同時,也將Author刪除,其實是不符合要求的,這時候應(yīng)該將Author作為值對象看待,Author的Id就是一個值,然后建立一個類AuthorRef ,包含這個值,作為被Book引用的對象,這樣就不是整個Author實體聚合對象被Book引用了。

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習有所幫助,也希望大家多多支持VeVb武林網(wǎng)。


注:相關(guān)教程知識閱讀請移步到JAVA教程頻道。
發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 日本成人一二三区 | 天天干天天透 | 91久久国产露脸精品免费 | 久久久久久久久久亚洲 | 日本a大片 | 国产毛片网站 | 福利在线免费视频 | 九九热精品免费 | 亚洲小视频在线观看,com | 欧美黄色免费视频 | 久久久成人精品视频 | 色97色| 中文字幕精品在线视频 | 最新中文在线视频 | 成人在线视频在线观看 | 免费国产自久久久久三四区久久 | 羞羞视频.www在线观看 | 性爱视频免费 | 色骚综合 | 视频一区免费观看 | 欧美一区二区三区免费不卡 | 欧美日韩成人一区二区 | 狠狠干网站 | 国产99久久久国产精品 | 双性精h调教灌尿打屁股的文案 | 91久久国产露脸精品免费 | 国产精品久久久久久久久久10秀 | 亚洲国产高清一区 | 色综合精品 | www.com香蕉| 中文在线观看www | 羞羞答答tv | 日本一区二区高清不卡 | 欧美成人免费tv在线播放 | 欧美淫交 | 爱爱视频天天干 | 久久久久久免费免费 | 在线无码| 国产自91精品一区二区 | 久热久操 | 99seav |