定義 Repository 介面
要定義 Repository 介面,首先需要定義一個域類(domain class)特定的 Repository 介面。該介面必須繼承 Repository
,並宣告其處理的域類型別和 ID 型別。如果您希望為該域型別暴露 CRUD 方法,則可以繼承 CrudRepository
或其變體之一,而不是 Repository
。
細化 Repository 定義
有幾種方法可以開始定義您的 Repository 介面。
典型的方法是繼承 CrudRepository
,它提供了 CRUD 功能的方法。CRUD 代表建立(Create)、讀取(Read)、更新(Update)、刪除(Delete)。從 3.0 版本開始,我們還引入了 ListCrudRepository
,它與 CrudRepository
非常相似,但對於那些返回多個實體的方法,它返回一個 List
而不是 Iterable
,您可能會發現這更容易使用。
如果您使用的是響應式儲存(reactive store),則可以根據您使用的響應式框架選擇 ReactiveCrudRepository
或 RxJava3CrudRepository
。
如果您使用 Kotlin,可以選擇 CoroutineCrudRepository
,它利用了 Kotlin 的協程(coroutines)。
此外,如果您需要允許指定 Sort
抽象或(在第一種情況下)Pageable
抽象的方法,您可以繼承 PagingAndSortingRepository
、ReactiveSortingRepository
、RxJava3SortingRepository
或 CoroutineSortingRepository
。請注意,各種排序 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 方法(在本例中為 findById
和 save
)
@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 提供的您選擇的儲存(store)的基礎 Repository 實現(例如,如果您使用 JPA,實現是 SimpleJpaRepository
),因為它們匹配 CrudRepository
中的方法簽名。因此,UserRepository
現在可以儲存使用者,透過 ID 查詢單個使用者,並觸發查詢以透過電子郵件地址查詢 Users
。
中間的 Repository 介面使用 @NoRepositoryBean 註解。請確保將該註解新增到所有 Spring Data 在執行時不應建立例項的 Repository 介面上。 |
將 Repository 與多個 Spring Data 模組一起使用
在應用程式中使用唯一的 Spring Data 模組會很簡單,因為定義範圍內的所有 Repository 介面都繫結到該 Spring Data 模組。有時,應用程式需要使用多個 Spring Data 模組。在這種情況下,Repository 定義必須區分持久化技術。當檢測到類路徑上有多個 Repository 工廠時,Spring Data 會進入嚴格的 Repository 配置模式。嚴格配置使用 Repository 或域類上的詳細資訊來決定 Repository 定義的 Spring Data 模組繫結。
-
如果 Repository 定義繼承了模組特定的 Repository,則是特定 Spring Data 模組的有效候選者。
-
如果域類使用模組特定的型別註解進行標記,則是特定 Spring Data 模組的有效候選者。Spring Data 模組接受第三方註解(例如 JPA 的
@Entity
)或提供自己的註解(例如 Spring Data MongoDB 和 Spring Data Elasticsearch 的@Document
)。
以下示例展示了一個使用模組特定介面的 Repository(本例中為 JPA)
interface MyRepository extends JpaRepository<User, Long> { }
@NoRepositoryBean
interface MyBaseRepository<T, ID> extends JpaRepository<T, ID> { … }
interface UserRepository extends MyBaseRepository<User, Long> { … }
MyRepository
和 UserRepository
在其型別層級結構中都繼承了 JpaRepository
。它們是 Spring Data JPA 模組的有效候選者。
以下示例展示了一個使用通用介面的 Repository
interface AmbiguousRepository extends Repository<User, Long> { … }
@NoRepositoryBean
interface MyBaseRepository<T, ID> extends CrudRepository<T, ID> { … }
interface AmbiguousUserRepository extends MyBaseRepository<User, Long> { … }
AmbiguousRepository
和 AmbiguousUserRepository
在其型別層級結構中僅繼承了 Repository
和 CrudRepository
。雖然在使用唯一的 Spring Data 模組時這沒有問題,但多個模組無法區分這些 Repository 應該繫結到哪個特定的 Spring Data。
以下示例展示了一個使用帶有註解的域類的 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
interface JpaPersonRepository extends Repository<Person, Long> { … }
interface MongoDBPersonRepository extends Repository<Person, Long> { … }
@Entity
@Document
class Person { … }
此示例顯示了一個同時使用 JPA 和 Spring Data MongoDB 註解的域類。它定義了兩個 Repository,JpaPersonRepository
和 MongoDBPersonRepository
。一個用於 JPA,另一個用於 MongoDB。Spring Data 將無法區分這些 Repository,這會導致未定義的行為。
Repository 型別詳細資訊和區分域類註解用於嚴格的 Repository 配置,以識別特定 Spring Data 模組的 Repository 候選者。在同一個域型別上使用多個特定於持久化技術的註解是可能的,並且可以在多個持久化技術之間重用域型別。但是,在這種情況下,Spring Data 將無法確定要繫結 Repository 的唯一模組。
區分 Repository 的最後一種方法是透過限定 Repository 的基本包。基本包定義了掃描 Repository 介面定義的起點,這意味著 Repository 定義位於適當的包中。預設情況下,註解驅動配置使用配置類的包。基於 XML 的配置中的基本包是強制性的。
以下示例展示了基本包的註解驅動配置
@EnableJpaRepositories(basePackages = "com.acme.repositories.jpa")
@EnableMongoRepositories(basePackages = "com.acme.repositories.mongo")
class Configuration { … }