定義倉庫介面

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

微調倉庫定義

你可以通過幾種不同的方式開始使用你的倉庫介面。

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

如果你正在使用響應式儲存,根據你使用的響應式框架,可以選擇 ReactiveCrudRepositoryRxJava3CrudRepository

如果你正在使用 Kotlin,可以選擇利用 Kotlin 協程的 CoroutineCrudRepository

此外,如果你需要允許指定 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 將無法確定一個唯一的模組來繫結倉庫。

區分倉庫的最後一種方式是透過界定倉庫基礎包(base package)的範圍。基礎包定義了掃描倉庫介面定義的起始點,這意味著倉庫定義應位於相應的包中。預設情況下,註解驅動的配置使用配置類所在的包。基於 XML 的配置中的基礎包是必需的。

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

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