定義儲存庫介面
要定義一個儲存庫介面,首先需要定義一個特定於域類的儲存庫介面。該介面必須繼承 Repository
並宣告為域類和 ID 型別。如果你想為該域型別公開 CRUD 方法,可以繼承 CrudRepository
或其變體之一,而不是 Repository
。
微調儲存庫定義
有幾種不同的方式可以開始定義你的儲存庫介面。
典型的方法是繼承 CrudRepository
,它為你提供了 CRUD 功能的方法。CRUD 代表建立(Create)、讀取(Read)、更新(Update)、刪除(Delete)。從 3.0 版本開始,我們還引入了 ListCrudRepository
,它與 CrudRepository
非常相似,但對於返回多個實體的方法,它返回一個 List
而不是 Iterable
,這可能更容易使用。
如果你使用響應式儲存,你可以選擇 ReactiveCrudRepository
或 RxJava3CrudRepository
,具體取決於你使用的響應式框架。
如果你使用 Kotlin,你可以選擇 CoroutineCrudRepository
,它利用了 Kotlin 的協程。
此外,如果你需要允許指定 Sort
抽象或在第一種情況下指定 Pageable
抽象的方法,你可以繼承 PagingAndSortingRepository
, ReactiveSortingRepository
, RxJava3SortingRepository
或 CoroutineSortingRepository
。請注意,各種排序儲存庫不再像 Spring Data 3.0 之前的版本那樣繼承其各自的 CRUD 儲存庫。因此,如果你想同時擁有兩者的功能,則需要繼承這兩個介面。
如果你不想繼承 Spring Data 介面,也可以使用 @RepositoryDefinition
註解你的儲存庫介面。繼承其中一個 CRUD 儲存庫介面會暴露一套完整的方法來操作你的實體。如果你更喜歡選擇性地暴露方法,可以將你想要暴露的方法從 CRUD 儲存庫複製到你的域儲存庫中。這樣做時,你可以更改方法的返回型別。如果可能,Spring Data 會尊重返回型別。例如,對於返回多個實體的方法,你可以選擇 Iterable<T>
、List<T>
、Collection<T>
或 VAVR 列表。
如果你的應用程式中的許多儲存庫應該具有相同的方法集,你可以定義自己的基礎介面來繼承。這樣的介面必須使用 @NoRepositoryBean
註解。這可以防止 Spring Data 直接嘗試建立它的例項並失敗,因為它無法確定該儲存庫對應的實體,因為它仍然包含一個泛型型別變數。
以下示例展示瞭如何選擇性地暴露 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);
}
在前面的示例中,你為所有域儲存庫定義了一個通用的基礎介面,並暴露了 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 模組繫結
-
如果儲存庫定義繼承了模組特定的儲存庫,它是特定 Spring Data 模組的有效候選項。
-
如果域類使用模組特定的型別註解進行註解,它是特定 Spring Data 模組的有效候選項。Spring Data 模組接受第三方註解(例如 JPA 的
@Entity
)或提供自己的註解(例如 Spring Data MongoDB 和 Spring Data Elasticsearch 的@Document
)。
以下示例顯示了一個使用模組特定介面(在本例中是 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 模組的有效候選項。
以下示例顯示了一個使用通用介面的儲存庫
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 模組時這沒有問題,但多個模組無法區分這些儲存庫應該繫結到哪個特定的 Spring Data 模組。
以下示例顯示了一個使用帶有註解的域類的儲存庫
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
註解進行註解。
以下是一個不良示例,顯示了一個使用帶有混合註解的域類的儲存庫
interface JpaPersonRepository extends Repository<Person, Long> { … }
interface MongoDBPersonRepository extends Repository<Person, Long> { … }
@Entity
@Document
class Person { … }
此示例顯示了一個域類同時使用了 JPA 和 Spring Data MongoDB 註解。它定義了兩個儲存庫:JpaPersonRepository
和 MongoDBPersonRepository
。一個用於 JPA,另一個用於 MongoDB。Spring Data 將無法區分這些儲存庫,從而導致未定義的行為。
儲存庫型別詳細資訊和區分域類註解用於嚴格的儲存庫配置,以識別特定 Spring Data 模組的儲存庫候選項。在同一個域型別上使用多個特定於持久化技術的註解是可能的,並且允許跨多個持久化技術重用域型別。但是,Spring Data 將無法確定繫結儲存庫的唯一模組。
區分儲存庫的最後一種方法是透過限定儲存庫基礎包的範圍。基礎包定義了掃描儲存庫介面定義的起始點,這意味著儲存庫定義位於適當的包中。預設情況下,註解驅動的配置使用配置類的包。基於 XML 的配置中的基礎包是強制性的。
以下示例展示了註解驅動的基礎包配置
@EnableJpaRepositories(basePackages = "com.acme.repositories.jpa")
@EnableMongoRepositories(basePackages = "com.acme.repositories.mongo")
class Configuration { … }