在前面的章節(jié)中,我們在`spring data JPA`的幫助下,使用`Teacher`類來映射了`teacher`表,使用`Klass`類來映射了`klass`表。本節(jié)中我們繼續(xù)展示`spring data JPA`在數(shù)據(jù)操作上強(qiáng)大的能力。
由于`Teacher`類定義了相關(guān)的屬性以及為這些屬性定義了相應(yīng)的類型,所以`spring data JPA`才能夠根據(jù)`Teacher`類來對應(yīng)生成相關(guān)的`teacher`表。故與其說我們前面講的`Teacher`類是對應(yīng)`teacher` 表,不如說`Teacher`類映射的是`teacher`表**結(jié)構(gòu)**。
| 序號 | JAVA類 | 數(shù)據(jù)表 |
| --- | --- | --- |
| 1 | 類名Teacher | 表名teacher |
| 2 | 屬性名id | 字段名id |
| 3 | 屬性類型Long | 字段類型bigint(long) |
在數(shù)據(jù)庫中,一張數(shù)據(jù)表主要以下述兩種要素組成:①表的定義(字段、外健、索引等);②表中所存儲的數(shù)據(jù)。下面,讓我們共同學(xué)習(xí)`spring data JPA`是如何處理表中的存儲數(shù)據(jù)的。
# Repository
`Repository`直譯為`倉庫`,在`spring data JPA`中將存儲了數(shù)據(jù)的表看做一座數(shù)據(jù)倉庫,我們可以由這個倉庫中取數(shù)據(jù),也可以在這個倉庫中添加數(shù)據(jù),同時也可以進(jìn)行刪除、修改的操作。如想建`klass`數(shù)據(jù)表對應(yīng)的倉庫,則需要進(jìn)行如下操作:
首先同controller及entity一樣,我們建立repository包。
### 初始化JAVA接口文件
repository/KlassRepository.java
```java
package com.mengyunzhi.springBootStudy.repository;
import com.mengyunzhi.springBootStudy.entity.Klass;
import org.springframework.data.repository.CrudRepository;
/**
* 班級倉庫
*/
public interface? KlassRepository? extends? CrudRepository?<Klass?, Long?>{
}
```
* ? 類型定義為interface接口
* ? 按規(guī)范命名為`數(shù)據(jù)表名`+`Repository`(該命名僅為了方便記憶)
* ? 該接口extends繼承CrudRepository?接口的同時,繼承了CrudRepository對數(shù)據(jù)表操作的功能。
* ? 該數(shù)據(jù)倉庫對應(yīng)對Klass類對應(yīng)的klass數(shù)據(jù)表進(jìn)行操作
* ? 該數(shù)據(jù)表的主鍵類型為Long
### 自定義查詢方法
接本節(jié)定義的前后臺對接接口,我們需要將班級名稱中包括前臺傳入的值的所有班級數(shù)據(jù)查詢出來,那么我們可以在KlassRepository中如下定義查詢方法:
repository/KlassRepository.java
```java
import java.util.List;
...
public interface KlassRepository extends CrudRepository<Klass, Long>{
List<Klass>? findAllByNameContains?(String name?);
}
```
* ? 定義返回值為`List<Klass>`來說明我要查詢多條班級數(shù)據(jù)。
* ? 定義方法名為:查詢出字段`name`中包含有第一個參數(shù)的所有數(shù)據(jù)。
* ? 定義第一個傳入?yún)?shù)的類型。
### 調(diào)用
我們來到想對班級表進(jìn)行查詢操作的controller/KlassController中,使用下面的方法進(jìn)行數(shù)據(jù)表的查詢操作:
controller/KlassController.java
```
import org.springframework.beans.factory.annotation.Autowired;
@RestController
@RequestMapping("Klass")
public class KlassController {
private static final Logger logger = LoggerFactory.getLogger(KlassController.class);
@Autowired ?
KlassRepository klassRepository;
@GetMapping
public List<Klass> getAll(@RequestParam String name) {
return this.klassRepository.findAllByNameContains(name);?
}
}
```
* ? 自動的向當(dāng)前對象中注入一個實現(xiàn)了KlassRepository接口的對象。
* ? 調(diào)用該對象的findAllByNameContains()方法來完成數(shù)據(jù)的查詢操作并返回。
*****
在面向?qū)ο蟮恼Z言中,我們學(xué)習(xí)過通過`new`關(guān)鍵字來由`類`來實例化一個供我們使用的對象。`Spring boot`是面向接口開發(fā)的框架,它為我們管理了大多數(shù)的對象,所以`new`操作發(fā)生在了`Spring boot`框架的底層,而我們在使用該框架的過程中,只需要通過`@Autowired`來告知它我們需要一個什么功能的對象即可。就也就是所謂的`ioc 控制反轉(zhuǎn)` ---- 以前我們在編寫代碼的過程中來管理對象,現(xiàn)在這個管理對象的任務(wù)卻**反轉(zhuǎn)為**框架來完成了。
*****
### 刪除冗余的配置項
由于`spring data jpa`已經(jīng)包含了`JDBC`,所以我們此時可以在`pom.xml`刪除原來對`jdbc`依賴了。
pom.xml
```
<dependencies>
<dependency> ?
<groupId>org.springframework.boot</groupId> ?
<artifactId>spring-boot-starter-data-jdbc</artifactId> ?
</dependency> ?
```
原則上,如果你不刪除該依賴也不應(yīng)該出現(xiàn)問題。但實際的情況是,如果你不刪除該依賴則在應(yīng)用啟動時會出現(xiàn)以下錯誤:
**以下內(nèi)容選讀**
```
org.springframework.beans.factory.support.BeanDefinitionOverrideException: Invalid bean definition with name 'klassRepository' defined in null:
Cannot register bean definition [Root bean: class [org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null] for bean 'klassRepository':
There is already [Root bean: class [org.springframework.data.jdbc.repository.support.JdbcRepositoryFactoryBean]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null] bound.
```
它的意思是說:在進(jìn)行對象管理的時候進(jìn)行新的對象注冊的時候,發(fā)現(xiàn)了重名的對象klassRepository。這個原因我猜測是由于:由`JPA`管理的`JpaRepositoryFactoryBean`以與由`JDBC`管理的`JdbcRepositoryFactoryBean`分別掃描到了`KlassRepository`。他們兩個都想把它注冊到`spring`統(tǒng)一管理的對象中,但該對象只允許存在一個,所以就報錯了。解決該問題也可以在配置文件中增加一行:`spring.main.allow-bean-definition-overriding=true`,意思是說如果發(fā)現(xiàn)有重名的了,就用后面的覆蓋前面的。此做法并不推薦。
## 測試
我們重新啟動應(yīng)用,然后在數(shù)據(jù)表中添加2條測試數(shù)據(jù),并重新運行測試:
教師表:

班級表:

當(dāng)name為hello,返回0條數(shù)據(jù):
```
GET http://localhost:8080/Klass?name=hello
HTTP/1.1 200
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Mon, 04 Nov 2019 07:08:15 GMT
[]
Response code: 200; Time: 260ms; Content length: 2 bytes
```
當(dāng)name為空時,返回3條數(shù)據(jù):
```
GET http://localhost:8080/Klass?name=
HTTP/1.1 200
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Mon, 04 Nov 2019 07:09:47 GMT
[
{
"id": 1,
"name": "測試1"
},
{
"id": 2,
"name": "測試2"
},
{
"id": 3,
"name": "其它"
}
]
Response code: 200; Time: 59ms; Content length: 66 bytes
```
當(dāng)name為`測試`時,返回2條數(shù)據(jù)
```
GET http://localhost:8080/Klass?name=%E6%B5%8B%E8%AF%95
HTTP/1.1 200
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Mon, 04 Nov 2019 07:10:17 GMT
[
{
"id": 1,
"name": "測試1"
},
{
"id": 2,
"name": "測試2"
}
]
Response code: 200; Time: 23ms; Content length: 45 bytes
```
當(dāng)name為`其它`時返回1條數(shù)據(jù)
```
GET http://localhost:8080/Klass?name=%E5%85%B6%E5%AE%83
HTTP/1.1 200
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Mon, 04 Nov 2019 07:10:49 GMT
[
{
"id": 3,
"name": "其它"
}
]
Response code: 200; Time: 25ms; Content length: 22 bytes
```
當(dāng)name為 `試`時,返回2條數(shù)據(jù):
```
GET http://localhost:8080/Klass?name=%E8%AF%95
HTTP/1.1 200
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Mon, 04 Nov 2019 07:11:20 GMT
[
{
"id": 1,
"name": "測試1"
},
{
"id": 2,
"name": "測試2"
}
]
Response code: 200; Time: 21ms; Content length: 45 bytes
```
### SETER/GETER
對照我們自己定義的接口,剛剛的測試貌似完全的滿足了我們的要求。但問題是我們在前面想顯示的為如下數(shù)據(jù):

而返回的數(shù)據(jù)當(dāng)中,我們并沒有看到`教師`的信息,這樣的數(shù)據(jù)返回給前臺必然是無法滿足實際需求的。這一問題的出現(xiàn),暴露了以下問題:
**在接口的定義過程中,返回數(shù)據(jù)的值定義的過于寬泛,在細(xì)節(jié)上沒有實際的指導(dǎo)意義。這為以后的BUG埋下的伏筆!**
這個問題產(chǎn)生的原因在這:
entity/Klass.java
```
package com.mengyunzhi.springBootStudy.entity;
import javax.persistence.*;
@Entity
public class Klass {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne
private Teacher teacher;
private String name;
public Klass() {
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
```
通過觀察此文件我們發(fā)現(xiàn),`teacher`屬性是沒有`setter`及`getter`方法的。為了更好的說明`setter`及`getter`在整個程序執(zhí)行過程中發(fā)揮的作用,我們依次添加`getter`及`setter`函數(shù):
#### GETTER與SETTER
entity/Klass.java
```
public Teacher getTeacher() {
return teacher;
}
public void setTeacher(Teacher teacher) {
this.teacher = teacher;
}
```
然后重新運行后臺、重新添加測試數(shù)據(jù)后重新測試:
```
GET http://localhost:8080/Klass?name=%E6%B5%8B%E8%AF%95
HTTP/1.1 200
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Mon, 04 Nov 2019 08:22:42 GMT
[
{
"id": 1,
"teacher": {
"id": 1,
"name": "張三更新",
"sex": false,
"username": "newzhangsan",
"email": "newzhangsan@yunzhiclub.com",
"createTime": null,
"updateTime": null
},
"name": "測試1"
},
{
"id": 2,
"teacher": {
"id": 1,
"name": "張三更新",
"sex": false,
"username": "newzhangsan",
"email": "newzhangsan@yunzhiclub.com",
"createTime": null,
"updateTime": null
},
"name": "測試2"
}
]
Response code: 200; Time: 164ms; Content length: 331 bytes
```
測試結(jié)果中返回了教師數(shù)據(jù),依此我們可以得出結(jié)論:
** 如果沒有setter與getter方法,查詢到的數(shù)據(jù)將無法返回給前臺。**
## 請測試
那么到底是getter還是setter在真正的起做用呢?先猜猜,然后自己測試一下來驗證自己的猜測結(jié)果。
# 參考文檔
| 名稱 | 鏈接 | 預(yù)計學(xué)習(xí)時長(分) |
| --- | --- | --- |
| 源碼地址 | [https://github.com/mengyunzhi/spring-boot-and-angular-guild/releases/tag/step3.2.9](https://github.com/mengyunzhi/spring-boot-and-angular-guild/releases/tag/step3.2.9) | - |
| Accessing data with MySQL | [https://spring.io/guides/gs/accessing-data-mysql/](https://spring.io/guides/gs/accessing-data-mysql/)| 15 |
- 序言
- 第一章:Hello World
- 第一節(jié):Angular準(zhǔn)備工作
- 1 Node.js
- 2 npm
- 3 WebStorm
- 第二節(jié):Hello Angular
- 第三節(jié):Spring Boot準(zhǔn)備工作
- 1 JDK
- 2 MAVEN
- 3 IDEA
- 第四節(jié):Hello Spring Boot
- 1 Spring Initializr
- 2 Hello Spring Boot!
- 3 maven國內(nèi)源配置
- 4 package與import
- 第五節(jié):Hello Spring Boot + Angular
- 1 依賴注入【前】
- 2 HttpClient獲取數(shù)據(jù)【前】
- 3 數(shù)據(jù)綁定【前】
- 4 回調(diào)函數(shù)【選學(xué)】
- 第二章 教師管理
- 第一節(jié) 數(shù)據(jù)庫初始化
- 第二節(jié) CRUD之R查數(shù)據(jù)
- 1 原型初始化【前】
- 2 連接數(shù)據(jù)庫【后】
- 3 使用JDBC讀取數(shù)據(jù)【后】
- 4 前后臺對接
- 5 ng-if【前】
- 6 日期管道【前】
- 第三節(jié) CRUD之C增數(shù)據(jù)
- 1 新建組件并映射路由【前】
- 2 模板驅(qū)動表單【前】
- 3 httpClient post請求【前】
- 4 保存數(shù)據(jù)【后】
- 5 組件間調(diào)用【前】
- 第四節(jié) CRUD之U改數(shù)據(jù)
- 1 路由參數(shù)【前】
- 2 請求映射【后】
- 3 前后臺對接【前】
- 4 更新數(shù)據(jù)【前】
- 5 更新某個教師【后】
- 6 路由器鏈接【前】
- 7 觀察者模式【前】
- 第五節(jié) CRUD之D刪數(shù)據(jù)
- 1 綁定到用戶輸入事件【前】
- 2 刪除某個教師【后】
- 第六節(jié) 代碼重構(gòu)
- 1 文件夾化【前】
- 2 優(yōu)化交互體驗【前】
- 3 相對與絕對地址【前】
- 第三章 班級管理
- 第一節(jié) JPA初始化數(shù)據(jù)表
- 第二節(jié) 班級列表
- 1 新建模塊【前】
- 2 初識單元測試【前】
- 3 初始化原型【前】
- 4 面向?qū)ο蟆厩啊?/a>
- 5 測試HTTP請求【前】
- 6 測試INPUT【前】
- 7 測試BUTTON【前】
- 8 @RequestParam【后】
- 9 Repository【后】
- 10 前后臺對接【前】
- 第三節(jié) 新增班級
- 1 初始化【前】
- 2 響應(yīng)式表單【前】
- 3 測試POST請求【前】
- 4 JPA插入數(shù)據(jù)【后】
- 5 單元測試【后】
- 6 惰性加載【前】
- 7 對接【前】
- 第四節(jié) 編輯班級
- 1 FormGroup【前】
- 2 x、[x]、{{x}}與(x)【前】
- 3 模擬路由服務(wù)【前】
- 4 測試間諜spy【前】
- 5 使用JPA更新數(shù)據(jù)【后】
- 6 分層開發(fā)【后】
- 7 前后臺對接
- 8 深入imports【前】
- 9 深入exports【前】
- 第五節(jié) 選擇教師組件
- 1 初始化【前】
- 2 動態(tài)數(shù)據(jù)綁定【前】
- 3 初識泛型
- 4 @Output()【前】
- 5 @Input()【前】
- 6 再識單元測試【前】
- 7 其它問題
- 第六節(jié) 刪除班級
- 1 TDD【前】
- 2 TDD【后】
- 3 前后臺對接
- 第四章 學(xué)生管理
- 第一節(jié) 引入Bootstrap【前】
- 第二節(jié) NAV導(dǎo)航組件【前】
- 1 初始化
- 2 Bootstrap格式化
- 3 RouterLinkActive
- 第三節(jié) footer組件【前】
- 第四節(jié) 歡迎界面【前】
- 第五節(jié) 新增學(xué)生
- 1 初始化【前】
- 2 選擇班級組件【前】
- 3 復(fù)用選擇組件【前】
- 4 完善功能【前】
- 5 MVC【前】
- 6 非NULL校驗【后】
- 7 唯一性校驗【后】
- 8 @PrePersist【后】
- 9 CM層開發(fā)【后】
- 10 集成測試
- 第六節(jié) 學(xué)生列表
- 1 分頁【后】
- 2 HashMap與LinkedHashMap
- 3 初識綜合查詢【后】
- 4 綜合查詢進(jìn)階【后】
- 5 小試綜合查詢【后】
- 6 初始化【前】
- 7 M層【前】
- 8 單元測試與分頁【前】
- 9 單選與多選【前】
- 10 集成測試
- 第七節(jié) 編輯學(xué)生
- 1 初始化【前】
- 2 嵌套組件測試【前】
- 3 功能開發(fā)【前】
- 4 JsonPath【后】
- 5 spyOn【后】
- 6 集成測試
- 7 @Input 異步傳值【前】
- 8 值傳遞與引入傳遞
- 9 @PreUpdate【后】
- 10 表單驗證【前】
- 第八節(jié) 刪除學(xué)生
- 1 CSS選擇器【前】
- 2 confirm【前】
- 3 功能開發(fā)與測試【后】
- 4 集成測試
- 5 定制提示框【前】
- 6 引入圖標(biāo)庫【前】
- 第九節(jié) 集成測試
- 第五章 登錄與注銷
- 第一節(jié):普通登錄
- 1 原型【前】
- 2 功能設(shè)計【前】
- 3 功能設(shè)計【后】
- 4 應(yīng)用登錄組件【前】
- 5 注銷【前】
- 6 保留登錄狀態(tài)【前】
- 第二節(jié):你是誰
- 1 過濾器【后】
- 2 令牌機(jī)制【后】
- 3 裝飾器模式【后】
- 4 攔截器【前】
- 5 RxJS操作符【前】
- 6 用戶登錄與注銷【后】
- 7 個人中心【前】
- 8 攔截器【后】
- 9 集成測試
- 10 單例模式
- 第六章 課程管理
- 第一節(jié) 新增課程
- 1 初始化【前】
- 2 嵌套組件測試【前】
- 3 async管道【前】
- 4 優(yōu)雅的測試【前】
- 5 功能開發(fā)【前】
- 6 實體監(jiān)聽器【后】
- 7 @ManyToMany【后】
- 8 集成測試【前】
- 9 異步驗證器【前】
- 10 詳解CORS【前】
- 第二節(jié) 課程列表
- 第三節(jié) 果斷
- 1 初始化【前】
- 2 分頁組件【前】
- 2 分頁組件【前】
- 3 綜合查詢【前】
- 4 綜合查詢【后】
- 4 綜合查詢【后】
- 第節(jié) 班級列表
- 第節(jié) 教師列表
- 第節(jié) 編輯課程
- TODO返回機(jī)制【前】
- 4 彈出框組件【前】
- 5 多路由出口【前】
- 第節(jié) 刪除課程
- 第七章 權(quán)限管理
- 第一節(jié) AOP
- 總結(jié)
- 開發(fā)規(guī)范
- 備用