持久化實體

可以使用 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 列使用了自增(auto-increment)時,生成的值將在實體插入資料庫後設置到實體中。

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

實體狀態檢測詳細解釋瞭如何檢測實體是否是新的,或者是否預期已存在於資料庫中。

一個重要的約束是,實體儲存後,它就不再是新的了。請注意,實體是否為新實體是實體狀態的一部分。對於自增列,這是自動發生的,因為 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 Repositories 實現自定義方法的部分,讓您可以使用 JdbcAggregateTemplate 來自定義和擴充套件您的 Repositories。

持久化

JdbcAggregateTemplate 提供了三種用於持久化實體的方法:saveinsertupdate。每種方法都有兩種形式:一種操作單個聚合(名稱如上所述),另一種則帶有 All 字尾,操作一個 Iterable

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 運算子為可變引數建立一個條件。

  • 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 運算子為可變引數建立一個條件。

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

樂觀鎖

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

  • 聚合根的 update 語句將包含一個 where 子句,用於檢查資料庫中儲存的版本是否確實未改變。

  • 如果不是這種情況,將丟擲 OptimisticLockingFailureException

此外,版本屬性在實體和資料庫中都會增加,以便併發操作會注意到此變化,並在適用時丟擲 OptimisticLockingFailureException,如上所述。

這個過程也適用於插入新的聚合,其中 null0 版本表示新例項,之後增加的版本會將例項標記為不再是新的,這使得它非常適合那些在物件構造期間生成 ID 的情況,例如使用 UUID 時。

在刪除過程中,版本檢查也適用,但版本不會增加。