Spring Data 2021.0增加了對DDD聚合更多自動支援!

發表於2021-04-27

Spring Data 2021.0(代號為Pascal)是繼六個月的新節奏之後的第二個版本。它附帶了對許多現有介面和程式設計模型的改進。這篇部落格文章解釋了以下主題:

 

1. 為CrudRepository和ReactiveCrudRepository引入了新的刪除方法deleteAllById

在Spring Data 2.0發行版中,我們重新命名了CrudRepository方法以表示特定方法將接受的引數。重新命名後,刪除方法看起來像deleteById(ID id)和deleteAll(Iterable<? extends T> entities)。根據資料儲存的實際情況,這可以是批量刪除(通過查詢刪除),具體取決於資料儲存模組。

 

2.支援Spring Core Java Flight Recorder(JFR)指標

Java Flight Recorder(JFR)是一種工具,用於收集,診斷和配置有關正在執行的Java應用程式的資料。它與Java執行時的緊密整合允許在生產環境中以低開銷收集事件。

Spring Data儲存庫通常在應用程式啟動時進行引導,因此它們自然會增加啟動時間。Pascal發行版引入了與Spring Framework支援的整合,以支援捕獲啟動事件,此功能自5.3版本起可用

 

3.刪除Apache Solr

小組已決定停止維護Solr的模組。展望未來,我們建議使用Spring Data Elasticsearch作為全文搜尋安排的首選Spring Data模組。Spring Data Elasticsearch是一個積極維護的社群模組。

 

4.使用QueryByExample支援R2DBC 和Oracle

通過示例查詢是一種具有簡單介面的使用者友好查詢技術。它允許動態查詢建立,並且不需要編寫包含欄位名稱的查詢。實際上,“示例查詢”根本不需要您使用SQL編寫查詢。它可用於多個Spring Data模組。從Spring Data R2DBC 1.3開始,您可以通過Spring Data R2DBC的實現ReactiveQueryByExampleExecutor使用示例來查詢關係資料:

PersonRepository people  = …;
DatabaseClient client = …;

var skyler = new Person(null, "Skyler", "White", 45);
var walter = new Person(null, "Walter", "White", 50);
var flynn = new Person(null, "Walter Jr. (Flynn)", "White", 17);
var marie = new Person(null, "Marie", "Schrader", 38);
var hank = new Person(null, "Hank", "Schrader", 43);

var example = Example.of(new Person(null, null, "White", null));

people.count(example).as(StepVerifier::create)
  .expectNext(3L)
  .verifyComplete();


var example = Example.of(new Person(null, "Walter", "WHITE", null), matching()
  .withIgnorePaths("age"). //
  .withMatcher("firstname", startsWith())
  .withMatcher("lastname", ignoreCase()));

people.findAll(example).collectList()
  .as(StepVerifier::create)
  .consumeNextWith(actual -> {
    assertThat(actual).containsExactlyInAnyOrder(flynn, walter);
  })
  .verifyComplete();

 

5.啟用 Cassandra Prepared 

Apache Cassandra的Spring Data儘可能容納Cassandra特定的功能。自從它在2.0版中進行重大重寫,引入了預備語句快取,從而允許使用純CQL使用預備語句。

 

6. 支援jMolecules

Spring Data儲存庫抽象一直是專案中的核心概念。它是針對領域驅動設計(DDD)中提出的體系結構概念的程式設計模型:儲存庫抽象了集合的集合。實際上,Spring框架本身與源自DDD的其他一些抽象(例如服務)保持一致,並提供註釋以在使用者程式碼中表達它們。但是,使用者通常不喜歡使用特定於框架的註釋和抽象來表達那些概念。

jMolecules專案僅專注於提供註釋和基於型別的抽象與對技術不同的架構概念可以整合。它實質上顛倒了這種關係:使用者程式碼僅取決於jMolecules註釋和介面,然後在第二步中從廣泛的jMolecules整合庫或框架本身提供技術整合。

Association介面是jMolecules的領域驅動設計模組中的核心抽象之一,它的型別一AggregateRoot以及其Identifier是領域模型用來表達對一個強型別的方式聚集的關係:

class Order implements AggregateRoot<Order, OrderIdentifier> {

  OrderIdentifier id;
  Association<Customer, CustomerIdentifier> customer;
}

Spring Data 2021.0.0隨附了對的Association對映支援。它們被正確地檢測為Spring資料關聯,並通過使用支援例項的識別符號進行轉換。

要透明地啟用對這些抽象的支援,請新增org.jmolecules.integrations:jmolecules-spring到您的類路徑中。Spring Data的對映基礎結構會檢測到該情況,並在我們的物件對映工具的轉換部分中自動註冊必要的轉換器。

Association還為JPA提供了例項支援。但是,在這種情況下,Spring Data不提供實際的翻譯,而是通過AttributeConverter整合jMolecules本身提供的實現。使用其ByteBuddy擴充套件,您可以生成必要的AttributeConverter實現和註釋配置。

 

識別符號和聚合例項之間的對映

jMolecules的Identifier介面鼓勵在聚合中使用專用識別符號型別,如先前示例中使用的OrderIdentifier和CustomerIdentifier型別。當序列化Association,我們現在實際上可有效地通過呼叫Association.getId()和Identifier.getId()將例項變為CustomerIdentifier,其中值會持久。

為了物化持久儲存關聯,我們必須獲取原始的持久值,,可以使用名為….of(…)的靜態公開工廠方法建立建立一個CustomerIdentifier例項,並最終再次呼叫Association.of(…)。

所有這些轉換步驟都是在jmolecules-integrationsSpring Data中實現的,並由Spring Data透明地新增到Spring中ConversionService供框架使用。

假設OrderIdentifier在資料庫是以UUID表示String型別,這也意味著Spring DataDomainClassConverter能夠將完全實現的聚合例項自動繫結到Spring MVC控制器方法:

@RestController
class MyController {

  @GetMapping("/orders/{id}")
  HttpEntity<?> getOrders(@PathVariable("id") Order order) { /* … */ }
}

這裡GET請求/orders/462a692d-…會自動轉換462a692d-…到OrderIdentifier變數中,這是使用jMolecules轉換器,然後使用尋找聚合名Order的例項。

雖然這種生成機制在Spring Data中已經使用了一段時間,但2021.0.0版本為jMolecules的Identifier 實現新增了必要的附加整合。

 

Spring Data REST聚合到DTO的對映

前面提到的jMoleculesConverter實現還用於所有需要Spring Data REST獲取並將聚合識別符號轉換為URI的地方。該模組還附帶了一個新的Jackson序列化器,該序列化器允許通過適當地反序列化URI將Spring Data REST管理的聚合例項繫結到DTO中。

假設您Order已由Spring Data REST管理並通過如下所示/orders/…的客戶控制器公開:

@BasePathAwareController
class MyCustomController {

  @PostMapping("/orders")
  HttpEntity<?> postOrder(@RequestBody MyDto payload) {
    /* Process submission */
  }
}

@Data
class MyDto {
  List<Order> orders;
}

假設請求對API提交了以下JSON內容:

{
  "orders" : [
    "…/orders/462a692d-…"
  ]
}

儘管MyDto是普通的資料傳輸物件,但其例項中orders會包含由標識462a692d-…為元素的聚合例項。

 

相關文章