定義倉庫介面

要定義倉庫介面,首先需要定義特定於域類的倉庫介面。該介面必須繼承 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 list。

如果您的應用程式中的許多倉庫應該具有相同的方法集,您可以定義自己的基礎介面來繼承。這樣的介面必須使用 `@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 { … }