1. 程式人生 > >【Spring】Sping Data JPA 深入學習之①查詢方法原理探究

【Spring】Sping Data JPA 深入學習之①查詢方法原理探究

基本概念

  • Spring Data JPA是Spring公司開發的Java Persistence API 相對於 sun公司開發的JPA
  • Spring Data JPA 整合了Hibernate,換句話說,Spring Data JPA 的預設實現是用的Hibernate

一個疑問:為什麼JPA沒有實現類?

在專案中觀察到前輩們自定義的repository層介面沒有實現類,查閱原始碼和資料得知,這是因為 我們自定義的介面繼承了JpaRepository介面,而此介面有一個自帶的實現類SimpleJpaRepository,它實現了部分常用的CRUD持久化方法。
我們用自定義介面物件呼叫JpaRepository介面中的方法時,在自定義介面中若沒有找到重寫的方法,便會去呼叫父類的方法,而父類只有一個自帶的實現類SimpleJpaRepository,便會去呼叫此類的實現。這型別的方法常見的有save、saveAll等。
但是同時我又發現,有一些查詢方法單純靠方法名查詢

,比如findByNameAndPassword(),第一次見到時感覺自己要失業了,居然只看方法名就知道要怎麼查詢…於是深入探究總結了一下。

Spring Data JPA的查詢方式

  • SimpleJpaRepository實現的自帶方法 如save和saveAll等
  • 在繼承了JpaRepository自定義介面中按照下圖語法編寫的方法
    在這裡插入圖片描述
  • 使用@Query在自定義的介面上編寫所需要的SQL語句

三種查詢方式的實現原理

  • 第一種如上所述,是由SimpleJpaRepository實現的。那麼SimpleJpaRepository是怎麼實現的呢?拿save()舉例,找一下原始碼就可以發現,在運用模板類的基礎上,呼叫了EntityManager類的perisist()方法,如圖所示。

在這裡插入圖片描述

  • 而EntityManager是在javax.persistence包中的內容,應該是原生JPA的內容
  • 再跟蹤一下persist(),可以發現persist()的實現是由hibernate中Session介面定義的
    在這裡插入圖片描述
  • 如圖為其實現,看來是呼叫了hibernate的內容,hibernate具體內容我還不是很熟練,留待將來回顧。
    在這裡插入圖片描述

  • 第二種方法是最讓我摸不到頭腦的,沒有實現類,單靠方法名就可以實現查詢。經過原始碼的閱讀和資料的查閱,我發現它的實現是通過攔截器實現的。在真正的方法體呼叫之前,使用攔截器獲取當前方法,並解析其方法名,實現根據方法名執行查詢。在spring-data-commons-xxxx-RELEASE-sources.jar中,springframework–>data–>repository–>core–>support

    包裡的RepositoryFactorySupport類,如圖所示。
    在這裡插入圖片描述

  • 重點關注註釋中:在建立介面時,此類對Repository介面建立實現了配置的代理,然後將控制權交給QueryExecuterMethodInterceptor,讓攔截器根據查詢策略QueryLookupStrategy.Key解析其內部是否有需要配置的查詢方法。

  • QueryExecuterMethodInterceptor就是上文提到的攔截器,是實現了MethodInterceptor介面的RepositoryFactorySupport的一個內部類,如圖所示,其中的queries屬性將攔截的自定義方法Method類和查詢RepositoryQuery利用Map關聯起來。
    在這裡插入圖片描述

  • 接下來到了撥雲見日的時刻了,我們跟蹤RepositoryQuery,發現其是一個“爺爺”級別的介面,類似Repository介面,其中只有兩個方法,如圖所示,execute()和getQueryMethod()。
    在這裡插入圖片描述

  • 只有這麼兩個方法是一定完不成各種具體的複雜工作的,它的實現類必定各種各樣。經過資料的查閱和來回來去一頓亂翻,我找到了它的實現類中的PartTreeJpaQuery。
    在這裡插入圖片描述

  • 在PartTreeJpaQuery裡,有一條屬性叫做tree,型別是PartTree,如圖所示。PartTreeJpaQuery的建構函式中,呼叫了PartTree的建構函式,傳入了方法名稱,在PartTree的建構函式裡,完成了對方法名稱的解析。
    在這裡插入圖片描述

  • 上圖是PartTreeJpaQuery的建構函式,重點關注try裡的第一行。
    在這裡插入圖片描述

  • 上圖是PartTree類的屬性,可以看到,利用正則表示式來匹配方法名。

  • 現在,我們已經知道:

  • 首先,在自定義Repository介面被建立時,由RepositoryFactorySupport建立代理,並在其建構函式中對此代理加入一個攔截器;

  • 之後,此攔截器,根據查詢策略,對該介面的自定義方法進行解析,根據查詢策略選取不同的RepositoryQuery實現進行處理,即得到可執行的持久化語句。

到這裡可以推斷出,第三種採用顯示JPQL/SQL語句查詢的方法也是類似的思路,因為這兩種查詢都可以在查詢策略中設定

還有一些不明朗的問題,比如RepositoryFactorySupport是怎麼建立代理的;怎麼根據查詢策略選取不同的RepositoryQuery實現的,具體處理過程是怎麼樣的,還有待後續學習…(立個flag 等我學會了就更新)