SpringBoot實戰SpringDataJPA


SpringDataJPASpring Data的一個子項目,通過提供基於JPARepository極大的減少了JPA作為數據訪問方案的代碼量,你僅僅需要編寫一個接口集成下SpringDataJPA內部定義的接口即可完成簡單的CRUD操作,理論的東西不做多解釋,下面我們開始講解SpringBoot

構建項目

我們使用IntelliJ IDEA工具構建一個SpringBoot項目,預先導入Web、MySQL、JPA依賴,我們簡單使用一個RestController來實現JPA的配置,之前也有講解JPA的簡單使用,今天詳細的講解下具體的細節性的內容,項目結構如下圖1所示:

 
圖1

使用Druid數據源

我們使用之前章節的配置,加入Druid數據源配置到我們的項目中,復制第四章:使用Druid作為SpringBoot項目數據源(添加監控)項目中的application.yml配置文件到我們項目resources下,並且修改pom.xml添加Druid數據源依賴,如下圖2所示:

 
圖2

application.yml配置文件內容如下圖3所示(具體代碼請到碼雲下載地址:git.oschina.net/jnyqy/lessons):

 
圖3

使用JpaRepository

我們在配置使用JpaRepository之前需要對應我們的測試表添加實體映射,為了本章的方便我們直接使用第四章:使用Druid作為SpringBoot項目數據源(添加監控)內的表結構以及實體,sql文件在第四章項目源碼的resource目錄下,可以下載后自行加載到本地數據庫中,表結構如下圖4所示:

 
圖4

根據表結構創建對應的實體映射,簡單點,我們使用單表操作,SpringDataJPAHibernate的語法一致內部都是使用了JPA的實現。映射實體代碼如下圖5所示:

 
圖5

上述圖5的getter/setter方法我沒有貼出來,自行工具構建就可以了。我們的准備做好了,下面我們創建UserJPA接口,上圖5我已經創建到了jpa目錄內,創建完成后打開添加繼承自JpaRepositoryJpaRepository需要泛型接口參數,第一個參數是實體,第二則是主鍵的類型,UserJPA代碼如下圖6所示:

 
圖6

數據訪問的接口就算是實現了,我們繼承的JpaRepository接口內有又繼承了PagingAndSortingRepository接口以及QueryByExampleExecutor接口,這兩個接口是用來干什么的?而PagingAndSortingRepository接口內部又有一個繼承自CrudRepository接口。如果對架構有點了解的朋友應該都知道,這樣設計得好處。

CrudRepository

該接口內包含了最簡單的CRUD也就是Create、Read、Update、Delete方法,當然還有count、exists方法,如下圖7所示:

 
圖7

如果自行定義的JPA繼承了該接口就會擁有CrudRepository接口內的所有方法實現。

PagingAndSortingRepository

該接口繼承自CrudRepository接口,包含了最基本的CRUD方法的實現,該接口內部添加了兩個方法,如下圖8所示:

 
圖8

顧名思義,看到接口名稱就可以聯想了,這個方法就是為了分頁而設計的,當然不僅僅是分頁還有排序方法。

QueryByExampleExecutor

我們的JpaRepository接口繼承了該接口,這個接口提供條件查詢,復雜查詢方法,可以通過Example方式進行查詢數據,源碼如下圖9所示:

 
圖9

后面我們會講到QueryDSL,到那時你就會知道SpringDataJPA提供的復雜條件查詢並不是最好的選擇。

JpaRepository

我們自定義的接口繼承了它,也就是說我們的UserJPA擁有了JpaRepository接口及父類接口的所有方法實現,所以我們並不需要添加任何數據操作代碼就可以完成數據操作,JpaRepository接口對條件查詢以及保存集合數據添加了對應的方法,代碼如下圖10所示:

 
圖10

具體每個方法是用來做什么的,根據名稱就可以看到,這里就不做一一的講解了。下面我們需要測試我們創建的UserJPA是否可以完成我們上述說的數據操作。

創建測試控制器

我們直接在controller包下創建一個UserController控制器,添加@RestController注解支持,我們因為方便這里就不編寫Service層的代碼實現了,直接在Controller內注入UserJPA,代碼如下圖11所示:

 
圖11

我們在UserController內添加了JpaRepository內部實現的findAll方法,用來查詢全部用戶數據,下面我們啟動項目測試。

初嘗試運行測試

當你使用SpringBootApplication方式運行項目時控制台會輸出項目運行失敗的日志提示,這里我們需要注釋掉spring-boot-starter-tomcat依賴的scope屬性就可以了。

查詢數據

嘗試訪問用戶列表地址:127.0.0.1:8080/list,可以看到頁面輸出了一條數據,這條數據是我事先在數據庫中手動添加的,如下圖12所示:

 
圖12

添加數據

我們編寫簡單的添加數據方法在UserController內,代碼如下圖13所示:

 
圖13

我們在add方法內創建了一個UserEntity對象並對所有的字段都賦值。

注意:SpringDataJPA內有個save方法,這個方法不僅僅是用來添加數據使用,當我們傳入主鍵的值時則是根據主鍵的值完成更新數據操作。

我們重啟下項目,嘗試訪問127.0.0.1:8080/add地址,界面輸出內容如下圖14所示:

 
圖14

界面給了我們數據添加成功的提示,我們訪問list地址驗證是否已經添加成功,如下圖15所示:

 
圖15

可以看到,數據已經通過add方法添加到數據庫內。由於更新的操作與添加一致這里就不做講解了,你只需要傳入主鍵的值即可。

刪除數據

我們簡單實現刪除一條數據,在UserController內添加delete方法,方法接受一個主鍵參數,如下圖16所示:

 
圖16

當我們訪問/delete時傳入userId參數就可以刪除對應的數據,下面我們重啟下項目,訪問127.0.0.1:8080/delete?userId=5,界面輸入內容如下圖17所示:

 
圖17

數據已經成功的完成了刪除操作。

上面的操作一切都是SpringDataJPA為我們自動完成的,到目前為止我們並沒有編寫一句SQL,那么SpringDataJPA是否支持自定義SQL語句呢?答案必須是肯定的!因為它是這個的強大!

@Query注解自定義SQL

SpringDataJPA內部有兩種方式可以實現自定義SQL功能,我們先來講述使用注解的方式,后期在SpringDataJPA核心技術專題內再詳細的講解使用EntityManager是如何完成自定義SQL、調用存儲過程、視圖等等操作的。下面我們打開UserJPA接口,添加自定義查詢年齡大於20的數據,如下圖18所示:

 
圖18

@Query是用來配置自定義SQL的注解,后面參數nativeQuery = true才是表明了使用原生的sql,如果不配置,默認是false,則使用HQL查詢方式。我們在UserController內添加方法/age,測試我們的自定義SQL是否有效,代碼如下圖19所示:

 
圖19

重啟下項目,訪問127.0.0.1:8080/age,效果如下圖20所示:

 
圖20

@Query配合@Modifying

從名字上可以看到我們的@Query注解好像只是用來查詢的,但是如果配合@Modifying注解一共使用,則可以完成數據的刪除、添加、更新操作。下面我們來測試下自定義SQL完成刪除數據的操作,我根據名字、密碼字段共同刪除一個數據,接口代碼如下圖21所示:

 
圖21

我們再來編寫UserController添加對應的方法調用deleteQuery接口方法,如下圖22所示:

 
圖22

重啟下項目,訪問地址:127.0.0.1:8080/deleteWhere,界面輸出內容如下圖23所示:

 
圖23

界面竟然出現了異常,這是怎么回事呢?可以看到拋出的異常TranscationRequiredException,意思就是你當前的操作給你拋出了需要事務異常,SpringDataJPA自定義SQL時需要在對應的接口或者調用接口的地方添加事務注解@Transactional,來開啟事務自動化管理。下面我們在UserJPA內添加@Transactional注解,重啟項目再來訪問剛才的地址,效果如下圖24所示:

 
圖24

界面已經給我提示了刪除成功,我們查看下控制台看打印的SQL是否是我們自定義的,如下圖25所示:

 
圖25

我們自定義的SQL被成功的打印了,自定義SQL完成添加,更新操作時跟刪除一致,都需要添加@Query以及@Modifying注解配合使用。

自定義BaseRepository

項目在正常情況下不僅僅只繼承一個JpaRepository接口,下一章我們整合SpringDataJPA跟QueryDSL時就需要添加多個接口繼承了,那么我們業務數據接口每一個都去繼承幾個相同的接口?答案肯定是 NO,當然多個繼承也是可以的,不過對於系統設計還有代碼復用性來說並不是最好的選擇!

我們創建一個包名叫做base,在包內添加一個BaseRepository接口,並且接口繼承我們的JpaRepository,代碼如下圖26所示:

 
圖26

又出現了一個新的注解,@NoRepositoryBean,這個注解是用來干什么的呢?

Spring開源程序猿在命名規則上應該是比較嚴格的,從名字上我們幾乎就可以判斷出用途,這個注解如果配置在繼承了JpaRepository接口以及其他SpringDataJpa內部的接口的子接口時,子接口不被作為一個Repository創建代理實現類。

我們創建的業務數據接口直接繼承BaseRepository就行了,繼承的子接口會擁有JpaRepository所有方法實現。

分頁查詢

分頁對於大型系統來說肯定是必不可少的,那么我們在SpringDataJpa內是如何使用分頁來完成查詢的呢?

一般情況我們會創建一個BaseEntity,在BaseEntity內添加幾個字段:排序列,排序方式,當前頁碼,每頁條數等,下面我們也來創建這么一個父類,代碼如下圖27所示:

 
圖27

我們修改UserEntity繼承BaseEntity,然后在數據庫內添加上幾條測試數據,我們每次頁面的數量就不用20了,我們在創建查詢條件時修改成2條。

我們打開UserController添加cutPage方法,用於作為分頁查詢入口,(注意:文章的講解都沒有添加Service層所以所有的業務邏輯都在Controller內處理的,正式項目請不要這樣編寫。)我們在cutPage方法內添加對應的分頁邏輯,如下圖28所示:

 
圖28

接下來我們重啟下項目,訪問地址:127.0.0.1:8080/cutpage?page=1,查看界面輸出效果如下圖29所示:

 
圖29

我們再來看下控制台的輸出,如下圖30所示:

 
圖30

可以看到控制台給我們打印了兩條SQL,第一條是分頁查詢的SQL,第二條是查詢表內總數量的SQL。SpringDataJPA內部對數量做出了封裝,你可以通過Page對象也就是PagingAndSortingRepository接口內的findAll(PageRequest request)方法的返回值類型中獲取到總條數、總頁數。

數據排序

我們上面在BaseEntity內添加了排序的字段以及排序方式,我們重新編輯下cutPage方法,修改pageRequest創建方式,添加Sort對象到PageRequest對象內,就可以實現排序數據。如下圖31所示:

 
圖31

上圖31可以看到我們修改了排序字段我們使用了默認的id,(注意:這里的排序字段不是數據庫內的字段名而是實體內的屬性名)以及排序方式改成了倒序,SpringDataJPA對排序方式添加了一個枚舉類型,創建Sort對象時也需要枚舉對象,因為我們BaseEntity配置的是字符串所以上面多了一步判斷排序方式返回枚舉對象。

重啟下項目,再來訪問分頁路徑,界面輸出效果如下圖32所示:

 
圖32

可以看到數據已經是倒序方式展示了,控制台的日志輸出也對應的添加了order by語句,如下圖33所示:

 
圖33

總結

綜上所述本章的內容已經講解完了,本章的內容比較多昨天完成沒有編寫完,還請見諒。本章主要講解了SpringBoot項目中使用SpringDataJPA的基本操作,包括了:CURD、分頁、排序、自定義SQL、定義BaseRepository、事務處理等。本章內容並不是SpringDataJPA全面內容,后續我會在SpringDataJPA核心技術專題詳細講解。

本章內容已經更新到碼雲:

SpringBoot配套源碼地址:https://gitee.com/hengboy/spring-boot-chapter

SpringCloud配套源碼地址:https://gitee.com/hengboy/spring-cloud-chapter

SpringBoot相關系列文章請訪問:目錄:SpringBoot學習目錄

QueryDSL相關系列文章請訪問:QueryDSL通用查詢框架學習目錄

SpringDataJPA相關系列文章請訪問:目錄:SpringDataJPA學習目錄

SpringBoot相關文章請訪問:目錄:SpringBoot學習目錄,感謝閱讀!





免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM