定義儲存庫介面

要定義儲存庫介面,您首先需要定義一個特定於領域類別的儲存庫介面。該介面必須擴展 Repository,並針對領域類別和 ID 類型進行類型化。如果您想為該領域類型公開 CRUD 方法,您可以擴展 CrudRepository 或其變體之一,而不是 Repository

微調儲存庫定義

您可以通過幾種變體開始使用您的儲存庫介面。

典型的方法是擴展 CrudRepository,它為您提供 CRUD 功能的方法。CRUD 代表 Create(建立)、Read(讀取)、Update(更新)、Delete(刪除)。在 3.0 版本中,我們還引入了 ListCrudRepository,它與 CrudRepository 非常相似,但對於那些返回多個實體的方法,它返回 List 而不是 Iterable,您可能會覺得更容易使用。

如果您使用的是反應式儲存,您可以選擇 ReactiveCrudRepositoryRxJava3CrudRepository,具體取決於您使用的反應式框架。

如果您使用 Kotlin,您可以選擇 CoroutineCrudRepository,它利用了 Kotlin 的協程。

此外,如果您需要允許指定 Sort 抽象或在第一種情況下 Pageable 抽象的方法,您可以擴展 PagingAndSortingRepositoryReactiveSortingRepositoryRxJava3SortingRepositoryCoroutineSortingRepository。請注意,各種排序儲存庫不再像 Spring Data 3.0 之前的版本那樣擴展它們各自的 CRUD 儲存庫。因此,如果您想要兩者的功能,則需要同時擴展這兩個介面。

如果您不想擴展 Spring Data 介面,您也可以使用 @RepositoryDefinition 註解您的儲存庫介面。擴展其中一個 CRUD 儲存庫介面會公開一整套方法來操作您的實體。如果您希望有選擇性地公開方法,請從 CRUD 儲存庫將您要公開的方法複製到您的領域儲存庫中。這樣做時,您可以更改方法的返回類型。如果可能,Spring Data 將會遵循返回類型。例如,對於返回多個實體的方法,您可以選擇 Iterable<T>List<T>Collection<T> 或 VAVR 清單。

如果您的應用程式中的許多儲存庫應該具有相同的方法集,您可以定義自己的基礎介面來繼承。這樣的介面必須使用 @NoRepositoryBean 註解。這可以防止 Spring Data 嘗試直接建立它的實例,並因無法確定該儲存庫的實體而失敗,因為它仍然包含泛型類型變數。

以下範例示範如何選擇性地公開 CRUD 方法(在本例中為 findByIdsave

選擇性地公開 CRUD 方法
@NoRepositoryBean
interface MyBaseRepository<T, ID> extends Repository<T, ID> {

  Optional<T> findById(ID id);

  <S extends T> S save(S entity);
}

interface UserRepository extends MyBaseRepository<User, Long> {
  User findByEmailAddress(EmailAddress emailAddress);
}

在先前的範例中,您為所有領域儲存庫定義了一個通用的基礎介面,並公開了 findById(…) 以及 save(…)。 這些方法會被路由到 Spring Data 提供的您選擇的儲存的基礎儲存庫實作中(例如,如果您使用 JPA,則實作是 SimpleJpaRepository),因為它們與 CrudRepository 中的方法簽名相符。因此,UserRepository 現在可以儲存使用者、依 ID 尋找個別使用者,並觸發查詢以依電子郵件地址尋找 Users

中間儲存庫介面使用 @NoRepositoryBean 註解。請確保將該註解添加到 Spring Data 不應在運行時建立實例的所有儲存庫介面。

將儲存庫與多個 Spring Data 模組一起使用

在您的應用程式中使用唯一的 Spring Data 模組可以簡化事情,因為定義範圍內的所有儲存庫介面都綁定到 Spring Data 模組。有時,應用程式需要使用多個 Spring Data 模組。在這種情況下,儲存庫定義必須區分持久性技術。當它在類別路徑上偵測到多個儲存庫工廠時,Spring Data 會進入嚴格的儲存庫組態模式。嚴格組態使用儲存庫或領域類別的詳細資訊來決定儲存庫定義的 Spring Data 模組綁定

  1. 如果儲存庫定義擴展了模組特定的儲存庫,則它是特定 Spring Data 模組的有效候選者。

  2. 如果領域類別使用模組特定的類型註解進行註解,則它是特定 Spring Data 模組的有效候選者。Spring Data 模組接受第三方註解(例如 JPA 的 @Entity)或提供自己的註解(例如 Spring Data MongoDB 和 Spring Data Elasticsearch 的 @Document)。

以下範例示範了一個使用模組特定介面的儲存庫(在本例中為 JPA)

範例 1. 使用模組特定介面的儲存庫定義
interface MyRepository extends JpaRepository<User, Long> { }

@NoRepositoryBean
interface MyBaseRepository<T, ID> extends JpaRepository<T, ID> { … }

interface UserRepository extends MyBaseRepository<User, Long> { … }

MyRepositoryUserRepository 在其類型層次結構中擴展了 JpaRepository。它們是 Spring Data JPA 模組的有效候選者。

以下範例示範了一個使用泛型介面的儲存庫

範例 2. 使用泛型介面的儲存庫定義
interface AmbiguousRepository extends Repository<User, Long> { … }

@NoRepositoryBean
interface MyBaseRepository<T, ID> extends CrudRepository<T, ID> { … }

interface AmbiguousUserRepository extends MyBaseRepository<User, Long> { … }

AmbiguousRepositoryAmbiguousUserRepository 在其類型層次結構中僅擴展了 RepositoryCrudRepository。雖然在使用唯一的 Spring Data 模組時這很好,但多個模組無法區分這些儲存庫應綁定到哪個特定的 Spring Data。

以下範例示範了一個使用帶有註解的領域類別的儲存庫

範例 3. 使用帶有註解的領域類別的儲存庫定義
interface PersonRepository extends Repository<Person, Long> { … }

@Entity
class Person { … }

interface UserRepository extends Repository<User, Long> { … }

@Document
class User { … }

PersonRepository 引用了 Person,它使用 JPA @Entity 註解進行了註解,因此此儲存庫顯然屬於 Spring Data JPA。UserRepository 引用了 User,它使用 Spring Data MongoDB 的 @Document 註解進行了註解。

以下不良範例示範了一個使用帶有混合註解的領域類別的儲存庫

範例 4. 使用帶有混合註解的領域類別的儲存庫定義
interface JpaPersonRepository extends Repository<Person, Long> { … }

interface MongoDBPersonRepository extends Repository<Person, Long> { … }

@Entity
@Document
class Person { … }

此範例示範了一個同時使用 JPA 和 Spring Data MongoDB 註解的領域類別。它定義了兩個儲存庫,JpaPersonRepositoryMongoDBPersonRepository。一個用於 JPA,另一個用於 MongoDB 用途。Spring Data 無法再區分這些儲存庫,這會導致未定義的行為。

儲存庫類型詳細資訊區分領域類別註解用於嚴格的儲存庫組態,以識別特定 Spring Data 模組的儲存庫候選者。在同一領域類型上使用多個持久性技術特定的註解是可能的,並且可以跨多個持久性技術重複使用領域類型。但是,Spring Data 無法再確定要將儲存庫綁定到的唯一模組。

區分儲存庫的最後一種方法是通過限定儲存庫基礎套件的範圍。基礎套件定義了掃描儲存庫介面定義的起點,這意味著儲存庫定義位於適當的套件中。預設情況下,註解驅動的組態使用組態類別的套件。基於 XML 的組態中的基礎套件是強制性的。

以下範例示範了基礎套件的註解驅動組態

基礎套件的註解驅動組態
@EnableJpaRepositories(basePackages = "com.acme.repositories.jpa")
@EnableMongoRepositories(basePackages = "com.acme.repositories.mongo")
class Configuration { … }