定義 Repository 介面

要定義一個 Repository 介面,首先需要定義一個特定於領域類的 Repository 介面。該介面必須擴充套件 Repository,並指定領域類和 ID 型別。如果想為該領域型別暴露 CRUD 方法,可以擴充套件 CrudRepository 或其變體,而不是 Repository

微調 Repository 定義

有幾種變體可以開始定義你的 Repository 介面。

典型的方法是擴充套件 CrudRepository,它提供了 CRUD 功能的方法。CRUD 代表建立(Create)、讀取(Read)、更新(Update)、刪除(Delete)。從 3.0 版本開始,我們還引入了 ListCrudRepository,它與 CrudRepository 非常相似,但對於返回多個實體的方法,它返回 List 而不是 Iterable,這可能更方便使用。

如果使用響應式儲存(reactive store),你可以選擇 ReactiveCrudRepositoryRxJava3CrudRepository,具體取決於你使用的響應式框架。

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

此外,如果需要允許指定 Sort 抽象(或在第一種情況下指定 Pageable 抽象)的方法,可以擴充套件 PagingAndSortingRepositoryReactiveSortingRepositoryRxJava3SortingRepositoryCoroutineSortingRepository。請注意,各種排序 Repository 在 Spring Data 3.0 版本之前不再擴充套件其各自的 CRUD Repository。因此,如果需要兩者的功能,你需要同時擴充套件這兩個介面。

如果你不想擴充套件 Spring Data 介面,也可以用 @RepositoryDefinition 註解你的 Repository 介面。擴充套件其中一個 CRUD Repository 介面會暴露一組完整的方法來操作你的實體。如果你希望有選擇地暴露方法,可以將你想暴露的方法從 CRUD Repository 中複製到你的領域 Repository 中。這樣做時,你可以改變方法的返回型別。Spring Data 將盡可能遵守返回型別。例如,對於返回多個實體的方法,你可以選擇 Iterable<T>List<T>Collection<T> 或 VAVR list。

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

以下示例展示瞭如何選擇性地暴露 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);
}

在前面的示例中,你為所有領域 Repository 定義了一個公共的基礎介面,並暴露了 findById(…)save(…) 方法。這些方法被路由到 Spring Data 提供的你選擇的儲存庫的基礎 Repository 實現中(例如,如果你使用 JPA,實現是 SimpleJpaRepository),因為它們與 CrudRepository 中的方法簽名匹配。因此,UserRepository 現在可以儲存使用者,透過 ID 查詢單個使用者,並觸發查詢以透過電子郵件地址查詢 Users

中間的 Repository 介面用 @NoRepositoryBean 註解。請確保將該註解新增到所有 Spring Data 不應在執行時建立例項的 Repository 介面上。

結合多個 Spring Data 模組使用 Repository

在應用程式中使用唯一的 Spring Data 模組很簡單,因為所有在定義範圍內 Repository 介面都繫結到該 Spring Data 模組。有時,應用程式需要使用多個 Spring Data 模組。在這種情況下,Repository 定義必須區分不同的持久化技術。當在類路徑上檢測到多個 Repository 工廠時,Spring Data 進入嚴格的 Repository 配置模式。嚴格配置使用 Repository 或領域類的詳細資訊來決定 Repository 定義與哪個 Spring Data 模組繫結。

  1. 如果 Repository 定義 擴充套件了模組特定的 Repository,則它是特定 Spring Data 模組的有效候選項。

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

以下示例展示了一個使用模組特定介面的 Repository(本例中為 JPA)

示例 1. 使用模組特定介面的 Repository 定義
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 模組的有效候選項。

以下示例展示了一個使用通用介面的 Repository

示例 2. 使用通用介面的 Repository 定義
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 模組時這是可以的,但多個模組無法區分這些 Repository 應該繫結到哪個特定的 Spring Data 模組。

以下示例展示了一個使用帶有註解的領域類的 Repository

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

@Entity
class Person { … }

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

@Document
class User { … }

PersonRepository 引用了 Person,該類用 JPA 的 @Entity 註解進行了標註,因此這個 Repository 顯然屬於 Spring Data JPA。UserRepository 引用了 User,該類用 Spring Data MongoDB 的 @Document 註解進行了標註。

以下這個反面示例展示了一個使用帶有混合註解的領域類的 Repository

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

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

@Entity
@Document
class Person { … }

此示例展示了一個同時使用 JPA 和 Spring Data MongoDB 註解的領域類。它定義了兩個 Repository:JpaPersonRepositoryMongoDBPersonRepository。一個用於 JPA,另一個用於 MongoDB。Spring Data 無法區分這兩個 Repository,這會導致未定義的行為。

Repository 型別細節區分領域類註解 用於嚴格的 Repository 配置,以識別特定 Spring Data 模組的 Repository 候選項。在同一個領域型別上使用多個特定於持久化技術的註解是可能的,並且可以在多個持久化技術中重用領域型別。然而,這樣 Spring Data 就無法確定要繫結該 Repository 的唯一模組。

區分 Repository 的最後一種方法是透過限定 Repository 基本包(base packages)。基本包定義了掃描 Repository 介面定義的起始點,這意味著 Repository 定義位於適當的包中。預設情況下,註解驅動的配置使用配置類所在的包。在 基於 XML 的配置 中,基本包是強制性的。

以下示例展示了註解驅動的基本包配置

註解驅動的基本包配置
@EnableJpaRepositories(basePackages = "com.acme.repositories.jpa")
@EnableMongoRepositories(basePackages = "com.acme.repositories.mongo")
class Configuration { … }