Spring Data Dijkstra 有哪些新功能?

工程 | Oliver Drotbohm | 2014 年 5 月 21 日 | ...

我們剛剛宣布 Spring Data 版本列車 Dijkstra 的 GA 版本可用。我想藉此機會帶您了解我們在此版本中添加的一些功能。

5 個新模組加入列車

此版本包含的第一個重要功能是向版本列車添加了 5 個模組。它們中的大多數已經存在很長時間了,但接下來我們將與其他模組同步發布它們。新添加的模組是 Spring Data ElasticsearchCassandraCouchbaseGemfireRedis

Spring Data Commons

版本列車的許多改進通常最終都在 Commons 模組中,以便各個儲存模組實際上都可以從新添加的功能中受益。以下是 Dijkstra 最重要的幾個:

支援 Wrapper 類型作為回傳值

對於宣告為回傳網域類型單一實例的 Spring Data 儲存庫方法,如果無法取得任何結果,儲存庫會管理回傳 null。但是,如果我們從頭開始 API,而不是直接回傳實例,我們可能更願意使用 Optional 來確保客戶端不會意外忘記 null 檢查。

Optional 是目前許多 Java 程式庫提供的一種類型。Google Guava 有它,更重要的是,JDK 8 也提供了一個。因此,透過 Dijkstra 版本列車,我們提供了使用這些類型作為回傳類型的包裝器,並讓 Spring Data 儲存庫基礎架構自動為您包裝 null 的可能性。

interface CustomerRepository extends Repository<Customer, Long> {

  Optional<Customer> findOne(Long id);

  Optional<Customer> findByEmailAddress(EmailAddress emailAddress);
}

您在這裡看到的第一個方法是 CrudRepository.findOne(…) 的變體。請注意,儲存庫介面不會擴充 CrudRepository,因為這會導致編譯錯誤,因為我們無法重新宣告 findOne(…) 方法並變更回傳類型。因此,我們建議您簡單地建立自己的基本儲存庫介面(如 參考文件 中所述,如果您想變更 findOne(…) 的行為)。

第二個方法是一個簡單的查詢方法,它使用查詢推導,讓 Spring Data 基礎架構從方法名稱推導查詢。我們也會在此處偵測到 Optional 作為包裝器類型,執行查詢並自動將結果包裝到 Optional 實例中。

非同步儲存庫方法調用

以類似的方式,我們開始向儲存庫方法添加非同步執行功能。查詢方法現在可以回傳 Future<T>,如果它使用 @Async 註釋,則會非同步執行該方法。

interface CustomerRepository extends Repository<Customer, Long> {

  @Async
  @Query(" … long running query declaration … ")
  Future<List<Customer>> findByLongRunningQuery(String lastname);
}

執行是基於 Spring 對非同步方法調用 的支持。我們也在研究更進階的非同步執行模型,例如承諾(可能基於 Project Reactor),以便在 Spring Data 的未來版本中添加。

地理空間類型

MongoDB 模組(以及其他模組)一直支援 Mongo 的地理空間操作 - 在 MongoTemplate 以及儲存庫抽象中。透過目前版本,我們將核心值類型(例如 ShapePointBoxDistance 等)移至 Spring Data Commons 模組中。這可讓您普遍參考地理類型,並與所有支援地理空間功能的 Spring Data 模組互動,而無需將這些類型相互映射。查看 JavaDoc 了解詳細資訊。

分片

Spring Data 一直在儲存庫程式設計模型中支援分頁。它允許您逐頁存取資料,以迭代大型資料集。除了純粹的頁面內容之外,Page 介面還公開了 API 來找出可用元素的總數和頁數。

計算此數字可能非常耗費資源(因為通常需要執行額外的查詢),而且通常關於頁面唯一有趣的元資訊是它是否具有下一頁,以便客戶端可以繼續檢索更多資料。您可以在 Facebook 時間軸中看到這種模式的範例。

Dijkstra 版本現在引入了 Page 的精簡版本,稱為 Slice,它允許您找出目前分片是否具有後續分片

interface BlogPostRepository extends Repository<BlogPost, Long> {

  Slice<BlogPost> findByAuthorOrderByDateDesc(Author author, Pageable pageable);
}

您現在可以使用 Pageable 來表達您想要取得的頁碼和大小,Spring Data 基礎架構將讀取比請求多一個項目,並使用它的存在或不存在作為指示器,以指示下一個可用的分片。

推導刪除查詢

查詢方法推導功能現在支援 deleteBy…removeBy… 方法前置詞,以推導基於給定標準移除受管理網域類型的查詢。

interface BlogPostRepository extends Repository<BlogPost, Long> {

  int deleteByDateBefore(Date date);
}

這已在 Dijkstra 的 JPA、MongoDB 和 Solr 模組中實作,並將在接下來添加到其他儲存模組。

JPA 2.1 支援

Dijkstra 的 JPA 模組開發期間的一個核心主題是支援 JavaEE 7 /JPA 2.1 功能。我們在此版本中處理的核心領域是支援查詢執行中的實體圖以及儲存程序的執行。

實體圖

假設我們有以下網域類型定義

@Entity
@NamedEntityGraph(name = "summary", attributeNodes = { 
  @NamedAttributeNode("firstname"),
  @NamedAttributeNode("lastname")})
class Customer {
  // properties ommitted
}

現在我們可以透過 @EntityGraph 註解來引用具名的實體圖,以表明我們希望將此圖應用於正在執行的查詢。

interface CustomerRepository extends Repository<Customer, Long> {

  @EntityGraph("summary")
  Optional<Customer> findByEmailAddress(EmailAddress emailAddress);
}

這將導致只有 firstnamelastname 屬性被 eager 載入,而所有其他屬性則準備好在存取時以 lazy 方式載入。

Stored procedures (預存程序)

JPA 2.1 增加了透過 EntityManager API 執行預存程序的能力。 與上面的範例類似,預存程序的元數據可以在 domain type 上宣告。 假設您想要觸發一個為客戶隨機創建密碼的預存程序。

@Entity
@NamedStoredProcedureQuery(name = "Customer.generateNewPassword", 
  procedureName = "generateNewPassword", parameters = {
    @StoredProcedureParameter(
      mode = ParameterMode.IN, name = "username", type = String.class),
    @StoredProcedureParameter(
      mode = ParameterMode.OUT, name = "password", type = String.class)})
class Customer {
  // properties ommitted
}

可以使用類似這樣的 repository 查詢方法來執行預存程序。

interface CustomerRepository extends Repository<Customer, Long> {

  @Procedure
  String generateNewPassword(@Param("username") String username);
}

預設情況下,我們將使用透過廣為人知的 DomainType.methodName 模式找到的預存程序元數據,並將其與方法宣告進行匹配。 對於像這裡展示的非常簡單的程序映射,您甚至可以省略元數據宣告,因為所有的元數據都可以從方法名稱推導出來。 請在參考文檔中找到更多關於預存程序支援的信息。

Transaction support for Redis (Redis 的交易支持)

最新版本的 Redis 模組增加了累計一組可以在批次中執行的操作的功能。 為了實現這一點,現在可以通過將 enableTransactionSupport 屬性配置為 true(預設為 false)來將 RedisTemplate 與 Spring 的事務同步集成。

啟用此功能將導致 RedisConnection 綁定到目前的 Thread,並發出 MULTI 命令,這允許底層的 Redis 驅動程式可能執行命令排隊。 如果事務在沒有錯誤的情況下完成,則會發出 EXEC 命令,如果失敗,則使用 DISCARD 命令丟棄累積的命令。

啟用後,連接將綁定到目前的 Thread,確保每個寫入操作都通過管道傳輸到相同的連接並排隊等待周圍的事務完成。 讀取操作(例如 KEYS 命令)仍將通過使用新的、非線程綁定的連接立即執行。

@Bean
public StringRedisTemplate redisTemplate() {

	StringRedisTemplate template = 
	  new StringRedisTemplate(redisConnectionFactory());
	// Enable transaction synchronization support
	template.setEnableTransactionSupport(true);

	return template;
}

像這樣配置的 RedisTemplate 然後可以與以下語義一起使用

// Executed on thread bound connection
template.opsForValue().set("foo", "bar");

// Read operation executed on separate connection
template.keys("*");

// Returns null as values set within transaction are not visible
// prior to transaction flush
template.opsForValue().get("foo");

Complex query support in Spring Data Solr (Spring Data Solr 中複雜查詢的支援)

使用 Spring Data Solr 的 criteria API 建立更複雜的查詢已經被要求了很長時間。 因此,我們決定重寫部分實現,使 API 與之前的版本兼容。

基本上,我們從相當扁平的表示方式轉向了類似樹狀的模型,保留了我們一直使用的所有 fluent API 風格。

一個 Solr 查詢 q=name:solr OR (type:spring AND category:data) 現在可以表示為

new SimpleQuery(
  where("name").is("solr").or(
    where("type").is("spring").and("category").is("data")));

Projections in Spring Data REST (Spring Data REST 中的投影)

Spring Data REST 公開的 REST 資源的一個非常常見的需求是能夠創建自定義的表示形式。 這意味著,用戶希望減少響應中呈現的屬性數量,或者內聯關聯的實體以節省伺服器往返。 透過 Spring Data REST 2.1,我們現在提供了一種在伺服器端定義自定義投影的可能性。 為此,您可以宣告一個介面來包含您想要公開的確切屬性

@Projection(name = "summary", types = Order.class)
interface OrderSummary {
	
  LocalDate getOrderedDate();

  CustomerSummary getCustomer();

  @Value("#{@shop.calculateTotal(target)}")
  Money getTotal();
}

這個介面可以放置在與 Order 相同的套件(或子套件)中,並且會被 Spring Data REST 自動檢測到。 它將導致所有公開單個訂單或訂單集合的資源在 URI 模板中攜帶一個額外的參數,以指示投影能力

{ _links : {
    orders : { href : "…/orders{?projection}", templated : true }
  }
}

如果客戶端現在使用 summary 擴展 projection 的模板,我們將在伺服器端創建一個代理,該代理將被移交給 Jackson 進行序列化。 每個 getter 都將轉發到實際目標類(在本例中為 Order)上的屬性查找。

在上面的範例中,getCustomer() 引用了一個相關的實體,該實體僅會在非投影場景中作為連結公開。 透過使用投影,我們檢測到該方法的返回類型不是 Customer。 這反過來會導致創建一個投影代理,以便您可以完全控制公開的屬性。 投影介面當然可以攜帶 Jackson 註解,以進一步自定義呈現的表示形式。

對於高級用例,您甚至可以使用 @Value 裝備投影方法,以返回 SpEL 表達式的結果給 marshaller。 在我們的範例中,我們調用了名為 shop 的 Spring bean 上的方法,並將代理目標實例傳遞給它以計算訂單總額,這可以考慮回扣、稅金等。

Summary (總結)

透過這些選定的範例,我希望我可以激發您的好奇心,探索 Dijkstra 發布列車中包含的模組。 我們現在將繼續我們的使命,透過啟動下一個名為 Evans 的發布列車,來簡化資料存取層的實作。

取得 Spring 電子報

隨時關注 Spring 電子報

訂閱

領先一步

VMware 提供培訓和認證,以加速您的進展。

了解更多

獲得支援

Tanzu Spring 在一個簡單的訂閱中提供對 OpenJDK™、Spring 和 Apache Tomcat® 的支援和二進位檔案。

了解更多

即將舉行的活動

查看 Spring 社群中所有即將舉行的活動。

查看所有