怎么在SpringData中使用JDBC實(shí)現(xiàn)DDD聚合-創(chuàng)新互聯(lián)

這篇文章將為大家詳細(xì)講解有關(guān)怎么在Spring Data中使用JDBC實(shí)現(xiàn)DDD聚合,文章內(nèi)容質(zhì)量較高,因此小編分享給大家做個(gè)參考,希望大家閱讀完這篇文章后對(duì)相關(guān)知識(shí)有一定的了解。

創(chuàng)新互聯(lián)是一家集網(wǎng)站建設(shè),曲沃企業(yè)網(wǎng)站建設(shè),曲沃品牌網(wǎng)站建設(shè),網(wǎng)站定制,曲沃網(wǎng)站建設(shè)報(bào)價(jià),網(wǎng)絡(luò)營(yíng)銷(xiāo),網(wǎng)絡(luò)優(yōu)化,曲沃網(wǎng)站推廣為一體的創(chuàng)新建站企業(yè),幫助傳統(tǒng)企業(yè)提升企業(yè)形象加強(qiáng)企業(yè)競(jìng)爭(zhēng)力??沙浞譂M(mǎn)足這一群體相比中小企業(yè)更為豐富、高端、多元的互聯(lián)網(wǎng)需求。同時(shí)我們時(shí)刻保持專(zhuān)業(yè)、時(shí)尚、前沿,時(shí)刻以成就客戶(hù)成長(zhǎng)自我,堅(jiān)持不斷學(xué)習(xí)、思考、沉淀、凈化自己,讓我們?yōu)楦嗟钠髽I(yè)打造出實(shí)用型網(wǎng)站。

Spring Data JDBC比JPA更容易理解,比如對(duì)象引用特性會(huì)很有趣。作為第一個(gè)示例,請(qǐng)考慮以下領(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;
}

另外,考慮如下定義的存儲(chǔ)庫(kù):

interface OrderRepository extends CrudRepository<PurchaseOrder, Long> {

 @Query("select count(*) from order_item")
 int countItems();
}

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

@Autowired OrderRepository repository;

@Test
public 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,它的所有項(xiàng)目也應(yīng)該被刪除。

 …
 repository.delete(saved);

 assertThat(repository.count()).isEqualTo(0);
 assertThat(repository.countItems()).isEqualTo(0);
}

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

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

當(dāng)書(shū)籍絕版時(shí),將Book刪除。所有的作者Author也都丟失了。這當(dāng)然不是你想要的,因?yàn)橐恍┳髡呖赡芤矊?xiě)過(guò)其他書(shū)。

怎么辦?

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

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

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

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

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

如果多個(gè)聚合引用同一個(gè)實(shí)體,則該實(shí)體不能成為引用它的多個(gè)聚合的一部分,因?yàn)樗荒苁瞧渲幸粋€(gè)聚合的一部分。因此,任何“多對(duì)一”和“多對(duì)多”關(guān)系都只能通過(guò)引用id來(lái)建模實(shí)現(xiàn)了。

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

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

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

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

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

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

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

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

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

使用Spring Data JDBC,您可以建模多對(duì)多關(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;
}

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

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

下面是測(cè)試:

@Test
public 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);
}

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

關(guān)于怎么在Spring Data中使用JDBC實(shí)現(xiàn)DDD聚合就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,可以學(xué)到更多知識(shí)。如果覺(jué)得文章不錯(cuò),可以把它分享出去讓更多的人看到。

當(dāng)前文章:怎么在SpringData中使用JDBC實(shí)現(xiàn)DDD聚合-創(chuàng)新互聯(lián)
轉(zhuǎn)載來(lái)于:http://muchs.cn/article38/dcpipp.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供靜態(tài)網(wǎng)站、App開(kāi)發(fā)、域名注冊(cè)網(wǎng)站制作、網(wǎng)站營(yíng)銷(xiāo)網(wǎng)站設(shè)計(jì)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶(hù)投稿、用戶(hù)轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

成都定制網(wǎng)站網(wǎng)頁(yè)設(shè)計(jì)