天天看點

《Spring Data實戰》——2.3 定義Repository

本節書摘來自異步社群《spring data實戰》一書中的第2章,第2.3節,作者: 【美】mark pollack , oliver gierke , thomas risberg , jon brisbin , michael hunger著,更多章節内容可以通路雲栖社群“異步社群”公衆号檢視

到目前為止,我們看到了帶有查詢方法的repository接口,這些查詢有的是從方法名中衍生出來的,有的是手動聲明的,這取決于spring data為實際存儲類型所提供的使用方式。為了衍生出這些查詢,我們必須擴充spring data的特定辨別接口:repository。除了查詢以外,在你的repository中還需要一些其他的功能:存儲對象,删除對象,根據id進行查找,傳回所有存儲的實體或按頁對它們進行通路。通過repository接口來暴露這些功能的最簡單方式就是使用一個spring data所提供的更為進階的repository接口。

repository

一個簡單的辨別接口,允許spring data的基礎設施擷取使用者定義的repository。

crudrepository

擴充自repository并添加了基本的持久化方法如對實體的儲存、查找以及删除。

pagingandsortingrepositories

擴充自crudrepository并添加了按頁通路實體以及根據給定的條件(criteria)進行排序的方法。

假設我們想讓customerrepository暴露基本的crud方法,所需要做就是修改其聲明,如示例2-12所示。

示例2-12 暴露crud方法的customerrepository

《Spring Data實戰》——2.3 定義Repository

crudrepository接口如示例2-13所示。它包括了儲存單個實體以及多個iterable實體的方法、擷取單個實體或所有實體的方法以及不同形式的delete(...)方法。

示例2-13 crudrepository

《Spring Data實戰》——2.3 定義Repository

支援repository方式的每個spring data子產品都提供了這個接口的實作。是以,我們聲明的命名空間元素會觸發基礎設施,這些設施不僅會啟動那些用于執行查詢方法的合适代碼,同時還會使用一個通用repository實作類的執行個體來在背後執行crudrepository中所聲明的方法,最終會将save(...)、findall()等方法的調用委托給該執行個體。pagingandsortingrepository(如示例2-14所示)擴充了crudrepository并為通用的findall(...)添加了處理pageable和sort執行個體的方法,進而能夠實作逐頁通路實體。

示例2-14 pagingandsortingrepository

《Spring Data實戰》——2.3 定義Repository

要将這些功能引入到customerrepository中,隻需簡單地擴充pagingandsorting repository來取代crudrepository即可。

2.3.1 調整repository接口

正如我們在前面所見,通過擴充合适的spring data接口,可以很容易地引入大量預先定義的功能。這種級别的粒度實際上是一種權衡,那就是如果為所有的查找方法、所有的儲存方法等都定義單獨的接口,我們會暴露接口的數量(以及是以導緻的複雜性)以及開發人員使用的便利性之間的權衡。

但是,可能會有這樣的場景,那就是隻想暴露讀方法(crud中的r)或者隻想在repository接口中将删除方法屏蔽掉。如今,spring data允許定義個性化的基礎repository,隻需按照以下的步驟操作即可。

1.建立一個接口,這個接口要麼擴充自repository,要麼添加@repositorydefinition注解。

2.添加想要暴露的方法并確定它們與spring data基礎repository接口所提供的方法簽名相同。

3.對于實體所對應的接口聲明,要使用這個接口作為基礎接口。

為了闡述這一點,假設我們隻想暴露接收pageable的findall(...)方法以及save方法。這個基礎接口看起來可能如示例2-15所示。

示例2-15 自定義基礎repository接口

《Spring Data實戰》——2.3 定義Repository

需要注意的一點是我們為這個接口添加了一個額外的注解@norepositorybean,進而確定spring data repository的基礎設施不會試圖為其建立bean的執行個體。讓customerrepository擴充這個接口就能精确做到隻暴露你所定義的api。

接下來可以定義出各種基本的接口(如readonlyrepository或saveonlyrepository)甚至組成它們的繼承體系,這取決于項目的需要。通常建議本地定義的crud方法在開始的時候直接位于每個實體的具體repository中,必要的話,再将它們要麼轉移到spring data提供的基礎repository中,要麼轉移到特制的repository中。按照這種方式,可以保證随着項目複雜性的增長,構件(artifact)的數量能夠自然地增長。

2.3.2 手動實作repository方法

到目前為止,看到了兩種類型的repository方法:crud方法和查詢方法。每種類型都是由spring data的基礎設施實作的,要麼通過背後的實作類,要麼通過查詢執行引擎。當建構應用程式的時候,這兩種場景可能會覆寫你所面臨的很大範圍的資料通路操作。但是,有些場景需要手動實作代碼。現在,讓我們看一下如何做到這一點。

我們開始隻實作那些需要手動實作的功能并在實作類中遵循一些命名的約定,如示例2-16所示。

示例2-16 為repository實作自定義功能

 

《Spring Data實戰》——2.3 定義Repository

接口和實作類均不需要了解spring data的任何事情。它與使用spring手動實作代碼非常類似。按照spring data來看,這個代碼片段最有意思的地方在于實作類的名字遵循了命名的約定,也就是在核心repository接口(在我們的場景中就是customerrepository)的名字上加impl字尾。同時需要注意,我們将接口和實作類都設為包内私有(package private),進而阻止從包外通路它們。

最後一步是修改初始repository接口的聲明,使其擴充剛剛引入的接口,如示例2-17所示。

示例2-17 在customerrepository中包含自定義功能

《Spring Data實戰》——2.3 定義Repository

現在,我們已經将customerrepositorycustom暴露的api引入到customerrepository之中了,這會使其成為customer資料通路api的中心點。用戶端代碼現在就可以調用customerrepository.mycustommethod(...)了。但是,這個實作類會如何被發現并置于最終執行的代理之中的呢?實際上,repository的啟動過程看起來是這樣的。

1.發現repository接口(如customerrepository)。

2.嘗試尋找一個bean定義,這個bean的名字為接口的小寫形式并添加impl字尾(如customerrepositoryimpl)。如果能夠找到,就使用它。

3.如果沒有找到,我們會掃描尋找一個類,這個類的名字為核心repository接口的名字并添加impl字尾(例如,在這個例子中customerrepositoryimpl會被找到)。如果找到了這樣的類,那麼将其注冊為spring bean并使用它。

4.找到的自定義實作類将會裝配到被發現接口的代理配置之中并且在方法調用時會作為潛在的目标類。

這種機制可以很容易地為特定repository實作自定義代碼。用于進行實作查找的字尾可以在xml命名空間中或啟用repository的注解屬性中(檢視各種存儲相關的章節來了解更多)進行個性化設定。