持久化實體

儲存聚合可以使用 CrudRepository.save(…) 方法來執行。如果聚合是新的,這會導致對聚合根進行插入,然後對所有直接或間接引用的實體執行插入語句。

如果聚合根不是新的,所有引用的實體都會被刪除,聚合根會被更新,然後所有引用的實體會再次被插入。請注意,實例是否為新的,是實例狀態的一部分。

這種方法有一些明顯的缺點。如果只有少數引用的實體實際上被更改了,刪除和插入是很浪費的。雖然這個過程可以而且可能會被改進,但 Spring Data JDBC 可以提供的東西有一定的限制。它不知道聚合的先前狀態。因此,任何更新過程都必須始終接受在資料庫中找到的任何內容,並確保將其轉換為傳遞給 save 方法的實體狀態。

另請參閱 實體狀態偵測 以取得更多詳細資訊。

載入聚合

Spring Data JDBC 提供兩種載入聚合的方式

  1. 傳統方式,也是在 3.2 版本之前的唯一方式,非常簡單:每個查詢都會載入聚合根,無論查詢是基於 CrudRepository 方法、衍生查詢還是註解查詢。如果聚合根引用了其他實體,這些實體將使用單獨的語句載入。

  2. Spring Data JDBC 3.2 允許使用單一查詢載入。使用此功能,可以使用單一 SQL 查詢完全載入任意數量的聚合。這應該會顯著提高效率,特別是對於由許多實體組成的複雜聚合。

    目前,單一查詢載入在不同方面受到限制

    1. 聚合不得具有巢狀集合,包括 Map。計劃在未來移除此限制。

    2. 聚合不得使用 AggregateReference 或嵌入式實體。計劃在未來移除此限制。

    3. 資料庫方言必須支援它。在 Spring Data JDBC 提供的方言中,除了 H2 和 HSQL 之外,所有方言都支援此功能。H2 和 HSQL 不支援分析函數(又稱視窗函數)。

    4. 它僅適用於 CrudRepository 中的 find 方法,不適用於衍生查詢,也不適用於註解查詢。計劃在未來移除此限制。

    5. 單一查詢載入需要在 JdbcMappingContext 中啟用,方法是呼叫 setSingleQueryLoadingEnabled(true)

如果任何條件未滿足,Spring Data JDBC 將退回預設的聚合載入方法。

單一查詢載入被認為是實驗性的。我們感謝您關於它如何為您工作的回饋。
雖然單一查詢載入可以縮寫為 SQL,但我們強烈不建議這樣做,因為與結構化查詢語言混淆幾乎是必然的。

ID 產生

Spring Data 使用識別符屬性來識別實體。實體的 ID 必須使用 Spring Data 的 @Id 註解來註解。

當您的資料庫的 ID 欄位具有自動遞增欄位時,產生的值會在插入到資料庫後設定到實體中。

當實體是新的且識別符值預設為其初始值時,Spring Data 不會嘗試插入識別符欄位的值。對於原始類型,初始值為 0,如果識別符屬性使用數值包裝類型(例如 Long),則初始值為 null

實體狀態偵測 詳細說明了偵測實體是否為新的,或是否預期存在於您的資料庫中的策略。

一個重要的限制是,在儲存實體後,該實體不得再是新的。請注意,實體是否為新的,是實體狀態的一部分。使用自動遞增欄位,這會自動發生,因為 ID 會由 Spring Data 設定為來自 ID 欄位的值。

Template API

作為 Repository 的替代方案,Spring Data JDBC 提供了 JdbcAggregateTemplate,作為在關聯式資料庫中載入和持久化實體的更直接方法。在很大程度上,Repository 使用 JdbcAggregateTemplate 來實作其功能。

本節僅重點介紹 JdbcAggregateTemplate 中最有趣的部分。如需更完整的概述,請參閱 JdbcAggregateTemplate 的 JavaDoc。

存取 JdbcAggregateTemplate

JdbcAggregateTemplate 旨在作為 Spring bean 使用。如果您已設定您的應用程式以包含 Spring Data JDBC,您可以在任何 Spring bean 中設定對 JdbcAggregateTemplate 的依賴性,而 Spring Framework 會注入一個正確設定的實例。

這包括您用來為您的 Spring Data Repository 實作自訂方法的片段,讓您可以使用 JdbcAggregateTemplate 來客製化和擴充您的 Repository。

持久化

JdbcAggregateTemplate 提供三種類型的持久化實體方法:saveinsertupdate。每種類型都有兩種形式:對單一聚合進行操作(名稱與上述完全相同),以及對 Iterable 進行操作的帶有 All 後綴的操作。

save 的作用與 Repository 中同名的方法相同。

insertupdate 跳過測試實體是否為新的步驟,並根據它們的名稱指示,假設為新的或現有的聚合。

查詢

JdbcAggregateTemplate 提供了相當多的方法來查詢聚合和聚合集合。有一種方法需要特別注意。那就是以 Query 作為引數的方法。它們允許執行以程式方式建構的查詢,如下所示

template.findOne(query(where("name").is("Gandalf")), Person.class);

query 方法回傳的 Query 定義了要選擇的欄位列表、where 子句(透過 CriteriaDefinition)以及 limit 和 offset 子句的規範。有關 Query 類別的詳細資訊,請參閱其 JavaDoc。

Criteria 類別,其中 where 是一個靜態成員,提供了 org.springframework.data.relational.core.query.CriteriaDefinition[] 的實作,它表示查詢的 where 子句。

Criteria 類別的方法

Criteria 類別提供以下方法,所有這些方法都對應於 SQL 運算子

  • Criteria and (String column):將具有指定 property 的鏈式 Criteria 新增到目前的 Criteria,並回傳新建立的 Criteria

  • Criteria or (String column):將具有指定 property 的鏈式 Criteria 新增到目前的 Criteria,並回傳新建立的 Criteria

  • Criteria greaterThan (Object o):使用 > 運算子建立條件。

  • Criteria greaterThanOrEquals (Object o):使用 >= 運算子建立條件。

  • Criteria in (Object…​ o):使用 IN 運算子為 varargs 引數建立條件。

  • Criteria in (Collection<?> collection):使用 IN 運算子為集合建立條件。

  • Criteria is (Object o):使用欄位比對 (property = value) 建立條件。

  • Criteria isNull ():使用 IS NULL 運算子建立條件。

  • Criteria isNotNull ():使用 IS NOT NULL 運算子建立條件。

  • Criteria lessThan (Object o):使用 < 運算子建立條件。

  • Criteria lessThanOrEquals (Object o):使用 運算子建立條件。

  • Criteria like (Object o):使用 LIKE 運算子建立條件,不進行跳脫字元處理。

  • Criteria not (Object o):使用 != 運算子建立條件。

  • Criteria notIn (Object…​ o):使用 NOT IN 運算子為 varargs 引數建立條件。

  • Criteria notIn (Collection<?> collection):使用 NOT IN 運算子為集合建立條件。

樂觀鎖定

Spring Data 透過在聚合根上使用 @Version 註解的數值屬性來支援樂觀鎖定。每當 Spring Data 儲存具有此版本屬性的聚合時,會發生兩件事

  • 聚合根的更新語句將包含一個 where 子句,檢查儲存在資料庫中的版本是否實際未更改。

  • 如果不是這種情況,將拋出 OptimisticLockingFailureException

此外,版本屬性會在實體和資料庫中都增加,因此並行操作將注意到更改,並在適用的情況下拋出 OptimisticLockingFailureException,如上所述。

此過程也適用於插入新的聚合,其中 null0 版本表示新的實例,而之後增加的實例將實例標記為不再是新的,這使得此功能在 ID 在物件建構期間產生的情況下(例如在使用 UUID 時)運作良好。

在刪除期間,版本檢查也適用,但不會增加版本。