前言
Java也是測試必知必會的內(nèi)容,特別是現(xiàn)在類似spring boot 等Java框架更是成為主流。之前實(shí)現(xiàn)的圖書增刪改查是用Python實(shí)現(xiàn)的,沒看過的請移步:Flask+mysql 實(shí)現(xiàn)增刪改查接口開發(fā)+測試(圖文教程附源碼),本次給大家?guī)碛肑ava實(shí)現(xiàn)的后端接口版本,并根據(jù)之前的項(xiàng)目總結(jié)有做一些優(yōu)化。有基礎(chǔ)的同學(xué)直接到第五章節(jié)CRUD開發(fā)
一、接口回顧
Python版本增刪改查成功示例
1.1 圖書增加
請求示例:
{
"title":"《Flask框架》",
"author":"pycharm",
"read_status":1
}
響應(yīng)示例:?
{
"message": "圖書添加成功!",
"status": "success"
}
完整截圖:
1.2 圖書刪除
請求示例:
{
"id":"49"
}
響應(yīng)示例:?
{
"message": "圖書被刪除!",
"status": "success"
}
完整截圖:
1.3 圖書修改
請求示例:
{
"id": 70,
"title": "《默8888888888》",
"author": "陳玉名",
"read_status": "1"
}
響應(yīng)示例:?
{
"message": "圖書已更新!",
"status": "success"
}
完整截圖:
1.4 圖書查詢
請求示例:
{
"id":"55"
}
響應(yīng)示例:?
{
"data": [
{
"author": "羅貫中",
"id": 55,
"read_status": true,
"title": "《三國演義》"
}
],
"message": "圖書查詢成功!",
"status": "success"
}
完整截圖:
1.5 分頁查詢
請求示例:
{
"page": 1,
"pagesize": 5
}
響應(yīng)示例:?
{
"data": [
{
"author": "羅貫中",
"id": 50,
"read_status": true,
"title": "《三國演義》"
},
{
"author": "羅貫中",
"id": 51,
"read_status": true,
"title": "《三國演義》"
},
{
"author": "羅貫中",
"id": 52,
"read_status": true,
"title": "《三國演義》"
},
{
"author": "羅貫中",
"id": 53,
"read_status": true,
"title": "《三國演義》"
},
{
"author": "羅貫中",
"id": 54,
"read_status": true,
"title": "《三國演義》"
}
],
"message": "圖書查詢成功!",
"page": 1,
"pagesize": 5,
"status": "success",
"total": 22
}
完整截圖:
?上面我們對相關(guān)接口的請求入?yún)ⅲ憫?yīng)復(fù)習(xí)了一遍,注意請求結(jié)構(gòu)、字段類型、請求方法、請求地址、接口響應(yīng)格式、接口響應(yīng)字段類型
下面我們就要用spring boot分層開發(fā)實(shí)現(xiàn)以上幾個接口,把查詢和分頁查詢合并一個接口,這也是一個優(yōu)化點(diǎn)
二、技術(shù)準(zhǔn)備
之前博客有簡單使用springboot、Mybatis等的文章,本篇文章再次講述下相關(guān)知識
2.1 spring boot
Spring Boot 是一個用于快速開發(fā)應(yīng)用程序的框架,它建立在 Spring 框架的基礎(chǔ)之上,旨在簡化配置和部署過程,使開發(fā)者能夠更專注于業(yè)務(wù)邏輯的實(shí)現(xiàn)。
以下是 Spring Boot 的一些主要特點(diǎn):
-
簡化配置: Spring Boot 提供了默認(rèn)的配置選項(xiàng),減少了開發(fā)者在配置方面的工作量。它使用約定大于配置的原則,根據(jù)項(xiàng)目的依賴和類型,自動配置應(yīng)用程序的不同部分。
-
內(nèi)嵌式 Web 服務(wù)器: Spring Boot 集成了多種內(nèi)嵌式 Web 服務(wù)器,如Tomcat、Jetty和Undertow,使得構(gòu)建和運(yùn)行 Web 應(yīng)用變得更加簡單。
-
自動化依賴管理: Spring Boot 可以管理項(xiàng)目的依賴,自動選擇合適的版本,簡化了依賴管理的復(fù)雜性。
-
自動化配置: Spring Boot 根據(jù)項(xiàng)目的依賴和需要,自動進(jìn)行一些常見配置,如數(shù)據(jù)源、消息隊(duì)列等,減少了手動配置的步驟。
-
開箱即用的功能: Spring Boot 提供了許多常見功能的開箱即用實(shí)現(xiàn),如安全認(rèn)證、緩存、監(jiān)控等,減少了開發(fā)者編寫大量樣板代碼的工作。
-
簡化部署: Spring Boot 支持將應(yīng)用程序打包為可執(zhí)行的 JAR 或 WAR 文件,以及支持 Docker 容器化部署,使部署變得更加方便。
-
微服務(wù)支持: Spring Boot 能夠與 Spring Cloud 結(jié)合使用,為微服務(wù)架構(gòu)提供支持,幫助開發(fā)者構(gòu)建分布式系統(tǒng)。
總之,Spring Boot 旨在簡化 Spring 應(yīng)用程序的開發(fā)、測試、部署和運(yùn)維過程,使開發(fā)者能夠更快速、更高效地開發(fā)出高質(zhì)量的應(yīng)用程序。它適用于各種規(guī)模的項(xiàng)目,從小型應(yīng)用到大型企業(yè)級系統(tǒng)。
2.2 Mybatis
MyBatis(之前稱為iBATIS)是一個開源的持久層框架,用于將 Java 對象映射到關(guān)系型數(shù)據(jù)庫中的數(shù)據(jù)。它提供了一種將 SQL 查詢、更新和刪除等操作與 Java 對象之間的映射關(guān)系的方式,從而使數(shù)據(jù)庫操作更加簡化和靈活。
以下是 MyBatis 的一些主要特點(diǎn):
-
靈活的SQL映射: MyBatis 允許開發(fā)者編寫原生的 SQL 查詢,而不需要過多的 ORM(對象關(guān)系映射)層。這使開發(fā)者能夠更精確地控制 SQL 查詢的執(zhí)行,并更好地優(yōu)化數(shù)據(jù)庫操作。
-
參數(shù)綁定: MyBatis 提供了參數(shù)綁定的功能,可以將 Java 對象中的屬性值綁定到 SQL 查詢中的參數(shù)位置,從而更方便地執(zhí)行動態(tài) SQL 查詢。
-
結(jié)果映射: MyBatis 可以將查詢結(jié)果映射為 Java 對象,開發(fā)者可以通過配置來指定查詢結(jié)果與 Java 對象之間的映射關(guān)系。
-
支持存儲過程和高級映射: MyBatis 支持存儲過程調(diào)用和一些復(fù)雜的結(jié)果映射,使開發(fā)者能夠處理更復(fù)雜的數(shù)據(jù)庫操作。
-
緩存支持: MyBatis 提供了一些級別的緩存支持,可以提高查詢的性能。
-
插件機(jī)制: MyBatis 允許開發(fā)者編寫插件來擴(kuò)展框架的功能,例如添加自定義的攔截器、執(zhí)行器等。
-
XML配置和注解: MyBatis 支持通過 XML 配置文件或者注解的方式來配置映射關(guān)系和查詢。
-
與各種數(shù)據(jù)庫兼容: MyBatis 支持多種數(shù)據(jù)庫,并且不需要太多的配置就可以適應(yīng)不同數(shù)據(jù)庫的特性。
總之,MyBatis是一個輕量級的持久層框架,它通過提供簡潔的方式來操作數(shù)據(jù)庫,使開發(fā)者能夠更加靈活和高效地進(jìn)行數(shù)據(jù)庫操作,而無需深入學(xué)習(xí)復(fù)雜的 ORM 框架。
2.3 Maven
Maven 是一個流行的項(xiàng)目構(gòu)建和管理工具,用于幫助開發(fā)團(tuán)隊(duì)管理項(xiàng)目的構(gòu)建、依賴管理和文檔生成等任務(wù)。它提供了一種標(biāo)準(zhǔn)化的方式來管理項(xiàng)目的生命周期,從而簡化了項(xiàng)目構(gòu)建、發(fā)布和維護(hù)過程。
以下是 Maven 的一些主要特點(diǎn)和功能:
-
項(xiàng)目結(jié)構(gòu)標(biāo)準(zhǔn)化: Maven 鼓勵使用一種特定的項(xiàng)目結(jié)構(gòu),包括源代碼目錄、資源文件目錄、測試目錄等。這有助于團(tuán)隊(duì)成員更容易理解和協(xié)同開發(fā)項(xiàng)目。
-
依賴管理: Maven 使用一個稱為 "坐標(biāo)" 的標(biāo)識符來唯一標(biāo)識項(xiàng)目的依賴庫。通過在項(xiàng)目的配置文件中聲明依賴,Maven 可以自動下載和管理所需的庫文件。
-
自動構(gòu)建: Maven 使用 POM(項(xiàng)目對象模型)文件來描述項(xiàng)目的構(gòu)建配置,包括源代碼目錄、依賴、插件等。開發(fā)者只需編寫 POM 文件,Maven 就能自動執(zhí)行構(gòu)建任務(wù)。
-
插件體系: Maven 提供了豐富的插件來執(zhí)行各種任務(wù),例如編譯、測試、打包、部署等。開發(fā)者可以通過配置插件來定制項(xiàng)目的構(gòu)建過程。
-
生命周期和階段: Maven 將項(xiàng)目的構(gòu)建過程劃分為生命周期(Lifecycle),每個生命周期又分為不同的階段(Phase)。例如,項(xiàng)目的生命周期包括 validate、compile、test、package、install、deploy 等階段。
-
倉庫管理: Maven 使用中央倉庫作為默認(rèn)的依賴庫,開發(fā)者可以將自己的構(gòu)建產(chǎn)物發(fā)布到本地倉庫或者遠(yuǎn)程倉庫。
-
多模塊支持: Maven 支持將一個大型項(xiàng)目劃分為多個子模塊,每個模塊可以有自己的 POM 文件和構(gòu)建配置,從而更好地管理復(fù)雜的項(xiàng)目結(jié)構(gòu)。
-
文檔生成: Maven 可以通過插件自動生成項(xiàng)目的文檔,包括代碼文檔、用戶文檔等。
總之,Maven 是一個用于項(xiàng)目構(gòu)建和管理的工具,它提供了一套規(guī)范和標(biāo)準(zhǔn),使開發(fā)者能夠更高效地進(jìn)行項(xiàng)目的構(gòu)建、依賴管理和發(fā)布等任務(wù)。
2.4 MySQL
MySQL 是一種開源的關(guān)系型數(shù)據(jù)庫管理系統(tǒng)(RDBMS),它是一種廣泛使用的數(shù)據(jù)庫技術(shù),用于存儲和管理結(jié)構(gòu)化數(shù)據(jù)。MySQL 最初由瑞典公司 MySQL AB 開發(fā),后來被 Sun Microsystems 收購,再后來 Sun Microsystems 被 Oracle Corporation 收購,因此 MySQL 也被稱為 Oracle MySQL。
以下是 MySQL 數(shù)據(jù)庫的一些主要特點(diǎn)和功能:
-
關(guān)系型數(shù)據(jù)庫: MySQL 是一種關(guān)系型數(shù)據(jù)庫,數(shù)據(jù)以表格的形式存儲,表格中的數(shù)據(jù)之間通過鍵值關(guān)系相互連接。
-
開源和免費(fèi): MySQL 是開源軟件,因此可以免費(fèi)使用和分發(fā)。開源性質(zhì)使得它受到廣泛的社區(qū)支持和貢獻(xiàn)。
-
跨平臺支持: MySQL 可以在多種操作系統(tǒng)上運(yùn)行,包括 Windows、Linux、macOS 等。
-
性能優(yōu)化: MySQL 在讀寫速度上具有優(yōu)異的性能,特別是對于高并發(fā)讀取請求和大規(guī)模數(shù)據(jù)操作。
-
數(shù)據(jù)安全性: MySQL 提供了數(shù)據(jù)的訪問控制、權(quán)限管理以及數(shù)據(jù)加密等功能,以確保數(shù)據(jù)的安全性和保密性。
-
事務(wù)支持: MySQL 支持事務(wù),可以保證在一系列操作中的原子性、一致性、隔離性和持久性(ACID 屬性)。
-
存儲引擎: MySQL 支持多種存儲引擎,如 InnoDB、MyISAM 等,不同的存儲引擎提供了不同的性能和特性。
-
SQL 支持: MySQL 使用結(jié)構(gòu)化查詢語言(SQL)作為與數(shù)據(jù)庫進(jìn)行交互的標(biāo)準(zhǔn)語言。
-
復(fù)制和集群: MySQL 提供了復(fù)制和集群功能,允許在多臺服務(wù)器之間同步數(shù)據(jù),實(shí)現(xiàn)高可用性和負(fù)載均衡。
-
備份和恢復(fù): MySQL 支持?jǐn)?shù)據(jù)備份和恢復(fù),使用戶能夠定期保存數(shù)據(jù)以應(yīng)對意外數(shù)據(jù)丟失。
總之,MySQL 是一款功能強(qiáng)大的開源關(guān)系型數(shù)據(jù)庫管理系統(tǒng),適用于各種規(guī)模的應(yīng)用,從個人項(xiàng)目到大型企業(yè)應(yīng)用都可以使用它來存儲和管理數(shù)據(jù)。
三、工具準(zhǔn)備
上面是理論,這一篇我們動手前檢查一下環(huán)境
3.1 IDEA
首先裝IDEA前,確保你本地有安裝java環(huán)境,本篇才用jdk 17,安裝教程有很多此處不再介紹
我用的是2020的pojie版本
3.2 Navicat
確保電腦安裝了mysql,或者其他機(jī)器上。Navicat安裝也簡單此處也不再贅述
主要是為了驗(yàn)證數(shù)據(jù)方便
這里為了和Python版本區(qū)分,我另起了一個book表,之前的是books,并把read_status設(shè)計(jì)為int型。在實(shí)際工作中也是這樣,有多個枚舉值的定義為int,后期好拓展。
重新設(shè)計(jì)的表DDL如下:
CREATE TABLE `book` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '自動遞增id,唯一鍵',
`title` varchar(80) NOT NULL COMMENT '書名',
`author` varchar(120) NOT NULL COMMENT '作者',
`read_status` int(11) DEFAULT NULL COMMENT '閱讀狀態(tài),0未讀,1已讀',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=73 DEFAULT CHARSET=utf8;
四、項(xiàng)目工程結(jié)構(gòu)
4.1 項(xiàng)目目錄結(jié)構(gòu)
4.2 項(xiàng)目目錄文件解析
4.2.1?config
包下有2個類:
DataSourceConfig.java、MyBatisConfig.java這2個是Mybatis調(diào)試過程中禁用了Spring Boot默認(rèn)的數(shù)據(jù)源自動配置 學(xué)習(xí)的文件,暫時不用管。
4.2.2?controller
包下有1個類:BookController ,用來處理前端請求的,前端或者postman請求首先會到達(dá)這一層,也叫控制層
4.2.3?domain
包 下有3個類:
BaseInput:封裝請求用的,實(shí)際項(xiàng)目會用上,此處沒時間封裝請求
Book:最主要的實(shí)體類或者模型類,字段和setter、getter方法都在這里,注意,實(shí)際項(xiàng)目會采用@Data注解等去掉setter、getter方法
ResponseData :封裝響應(yīng)類,實(shí)際項(xiàng)目也會有
4.2.4 mapper
包下有1個類:BookMapper
持久層,主要與數(shù)據(jù)庫進(jìn)行交互。也叫DAO層,現(xiàn)在用 Mybatis 逆向工程生成的 mapper 層,其實(shí)就是 DAO 層。DAO 層會調(diào)用entity(domain)層,DAO 中會定義實(shí)際使用到的方法,比如增刪改查
4.2.5 service
包下有1個類 BookService
,1個包impl,包下有服務(wù)實(shí)現(xiàn)類?BookServiceImpl、自定義異常類BookNotFoundException
4.2.6 啟動類
和上面 包平級的BookApplication類,是spring boot項(xiàng)目默認(rèn)生成的啟動類
4.2.7 resources下的mapper
有1個xml文件BookMapper.xml。和mapper(DAO)層交互,主要寫的是SQL語句
?4.2.8 resources 根目錄下的配置文件
application.properties文件或者application.yml文件。都可以,但是要注意2種文件的配置格式。這里采用的是yml文件,即application.yml
?4.2.9 依賴文件
pom.xml,存放一些依賴,可以在這里引入依賴,更改依賴
一定要注意目錄結(jié)構(gòu),否則會有各種報(bào)錯。
關(guān)于spring boot各層的詳細(xì)解析參考我的博文:Spring Boot 各層作用與聯(lián)系
五、增刪改查接口開發(fā)
前端請求過來遵循controller->mapper->service->entity我們開始寫代碼
5.1 圖書增加接口
5.1.1 controller.java
@RequestMapping("/api/book")
public class BookController {
private static final Logger logger = LoggerFactory.getLogger(BookController.class);
private final BookService bookService;
@Autowired
public BookController(BookService bookService){
this.bookService = bookService;
}
@PostMapping("/add")
public ResponseEntity<Map<String, String>> addBook(@RequestBody Book book) {
try {
//System.out.println("前端傳值ReadStatus:"+book.getReadStatus());
bookService.insertData(book);
Map<String, String> response = new HashMap<>();
response.put("message", "圖書添加成功!");
response.put("status", "success");
return ResponseEntity.ok(response);
} catch (IllegalArgumentException e) {
Map<String, String> response = new HashMap<>();
response.put("message", e.getMessage());
response.put("status", "fail");
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(response);
}
}
代碼點(diǎn)評:
這段代碼是一個 Spring Boot 控制器(Controller),用于處理關(guān)于圖書(Book)的 HTTP 請求。下面對其中的主要部分進(jìn)行解釋:
-
@RequestMapping("/api/book")
: 這個注解將類映射到 "/api/book" 路徑,表示這個控制器處理與圖書相關(guān)的 API 請求。 -
private final BookService bookService;
: 這是一個私有的成員變量,類型為BookService
,用于在控制器中調(diào)用與圖書相關(guān)的業(yè)務(wù)邏輯方法。 -
@Autowired
: 這個注解用于自動裝配BookService
實(shí)例,即將bookService
成員變量與 Spring 容器中的BookService
對象關(guān)聯(lián)起來。 -
@PostMapping("/add")
: 這個注解表示該方法處理 HTTP POST 請求,路徑為 "/add",用于添加圖書信息。 -
public ResponseEntity<Map<String, String>> addBook(@RequestBody Book book)
: 這是處理添加圖書請求的方法。它接收一個 HTTP 請求體中的 JSON 數(shù)據(jù),并將其映射為一個Book
對象。返回類型為ResponseEntity<Map<String, String>>
,即返回一個帶有消息和狀態(tài)信息的響應(yīng)。 -
try
: 這是一個異常處理的開始,表示嘗試執(zhí)行以下代碼塊,如果出現(xiàn)異常則跳到catch
部分進(jìn)行處理。 -
bookService.insertData(book);
: 調(diào)用bookService
的insertData
方法來將傳入的圖書信息插入到數(shù)據(jù)庫中。 -
Map<String, String> response = new HashMap<>();
: 創(chuàng)建一個HashMap
作為響應(yīng)體,用于存儲返回給客戶端的消息和狀態(tài)信息。 -
response.put("message", "圖書添加成功!");
: 將消息和狀態(tài)信息放入響應(yīng)體中。 -
response.put("status", "success");
: 將操作狀態(tài)放入響應(yīng)體中。 -
return ResponseEntity.ok(response);
: 返回一個 HTTP 響應(yīng),狀態(tài)碼為 200(成功),將上述響應(yīng)體包裝在ResponseEntity
中。 -
catch (IllegalArgumentException e) { ... }
: 這是異常處理的部分,如果在try
塊中出現(xiàn)了IllegalArgumentException
異常,就會執(zhí)行這里的代碼。 -
在
catch
部分中,創(chuàng)建一個包含錯誤消息和狀態(tài)的響應(yīng)體,然后使用ResponseEntity.status(HttpStatus.BAD_REQUEST).body(response)
返回一個 HTTP 響應(yīng),狀態(tài)碼為 400(客戶端請求錯誤),將錯誤信息包裝在ResponseEntity
中。
這段代碼定義了一個用于處理添加圖書請求的 Spring Boot 控制器方法。接收來自客戶端的圖書數(shù)據(jù),將數(shù)據(jù)傳遞給業(yè)務(wù)邏輯層進(jìn)行處理,然后返回適當(dāng)?shù)捻憫?yīng)消息和狀態(tài)信息。
5.1.2 BookService.java
void insertData(Book book);
?代碼點(diǎn)評:
void insertData(Book book);
是一個方法聲明,它位于某個 Java 接口(或抽象類)中,用于定義一個接口方法。這個方法聲明的含義如下:
-
void
: 方法返回類型。這里是void
,表示這個方法沒有返回值,即不返回任何數(shù)據(jù)。 -
insertData
: 方法名稱。這個方法被稱為insertData
,這是一個自定義的方法名,用于表示將一本圖書信息插入到某個數(shù)據(jù)源(如數(shù)據(jù)庫)中。 -
(Book book)
: 方法參數(shù)。這里的(Book book)
表示這個方法接受一個名為book
的參數(shù),參數(shù)類型是Book
類。這個參數(shù)將用于傳遞要插入的圖書信息。
這個方法聲明表示一個用于將圖書信息插入到數(shù)據(jù)源中的接口方法。實(shí)際的實(shí)現(xiàn)代碼應(yīng)該在某個類中,實(shí)現(xiàn)了包含這個方法聲明的接口,以便在調(diào)用時執(zhí)行插入操作。這個方法是在 BookService
接口中聲明的,具體的實(shí)現(xiàn)在 BookServiceImpl
類中
5.1.3?BookServiceImpl.java
@Override
public void insertData(Book book) {
if (book.getTitle() == null || book.getAuthor() == null || book.getReadStatus() == null) {
throw new IllegalArgumentException("title、author和read_status是必傳參數(shù)!");
}
// Check for duplicate title
String title = book.getTitle().trim();
if (bookMapper.existsByTitle(title)) {
throw new IllegalArgumentException("書名(title)重復(fù)!");
}
bookMapper.insertData(book);
}
代碼點(diǎn)評:?
這段代碼是一個方法的實(shí)現(xiàn),覆蓋(Override)了一個接口或父類中的同名方法。具體解釋如下:
-
@Override
: 這是一個注解,表示這個方法是對父類或接口中同名方法的重寫。在 Java 中,如果一個類實(shí)現(xiàn)了某個接口,或者一個類繼承了另一個類,可以通過使用@Override
注解來表明某個方法是對父類或接口中方法的重寫。 -
public void insertData(Book book) { ... }
: 這是一個方法的實(shí)現(xiàn)。它覆蓋了某個接口或父類中聲明的insertData
方法。方法簽名中的參數(shù)類型和返回類型必須與被重寫的方法一致。 -
方法體: 方法體包含了實(shí)際的代碼邏輯。這段代碼的目的是在插入圖書信息之前進(jìn)行一些驗(yàn)證和處理:
-
首先,檢查傳入的
book
對象的title
、author
和readStatus
是否為 null。如果有任何一個為 null,就會拋出IllegalArgumentException
異常,提示必要的參數(shù)不能為空。 -
然后,對傳入的
title
進(jìn)行修整,去掉前后的空格,以確保數(shù)據(jù)的準(zhǔn)確性。 -
接著,使用
bookMapper.existsByTitle(title)
方法檢查數(shù)據(jù)庫中是否已經(jīng)存在具有相同標(biāo)題的圖書。如果存在重復(fù)的標(biāo)題,會拋出IllegalArgumentException
異常,提示標(biāo)題重復(fù)。 -
最后,如果驗(yàn)證通過,調(diào)用
bookMapper.insertData(book)
方法將傳入的圖書信息插入到數(shù)據(jù)源中。
-
這段代碼實(shí)現(xiàn)了向數(shù)據(jù)源插入圖書信息的操作,并在插入前進(jìn)行了一些數(shù)據(jù)驗(yàn)證和處理,以確保數(shù)據(jù)的完整性和準(zhǔn)確性。
5.1.4 BookMapper.java
void insertData(Book book);
代碼點(diǎn)評:?
在 MyBatis 中,Mapper 接口的方法聲明定義了對數(shù)據(jù)庫的操作。這個 void insertData(Book book);
就是一個 Mapper 接口中的方法聲明,指定了一個用于向數(shù)據(jù)庫插入圖書信息的操作。
具體解釋如下:
-
void insertData(Book book);
: 這是一個接口中的方法聲明。它告訴 MyBatis 框架在映射配置文件中找到名為 "insertData" 的 SQL 語句,并將參數(shù)book
中的數(shù)據(jù)插入到數(shù)據(jù)庫中。void
: 表示該方法沒有返回值。insertData
: 方法的名稱,與映射配置文件中的<insert>
元素的 id 屬性對應(yīng)。(Book book)
: 方法的參數(shù),表示插入操作需要傳入一個名為book
的參數(shù),它的類型是Book
類(或其子類)。
這樣,在 MyBatis 映射配置文件中配置了一個名為 "insertData" 的 <insert>
元素,定義了插入操作的 SQL 語句,并在代碼中調(diào)用了 insertData(book)
方法時,MyBatis 會根據(jù)配置文件的定義,執(zhí)行對應(yīng)的 SQL 語句,將 book
對象的數(shù)據(jù)插入到數(shù)據(jù)庫中
5.1.5 BookMapper.xml
<!-- 增加 -->
<insert id="insertData">
INSERT INTO book (title, author, read_status)
VALUES (#{title}, #{author}, #{readStatus})
</insert>
代碼點(diǎn)評:?
這段 XML 代碼是 MyBatis 映射文件中的一個 SQL 映射,定義了一個插入操作,用于向數(shù)據(jù)庫中的 "book" 表中插入數(shù)據(jù)。具體解釋如下:
-
<insert id="insertData">
: 這是一個插入操作的聲明,通過id
屬性指定了該操作的標(biāo)識名,這樣在 Java 代碼中就可以通過這個標(biāo)識名來調(diào)用這個操作。 -
INSERT INTO book (title, author, read_status)
: 這是 SQL 語句的一部分,表示將數(shù)據(jù)插入到 "book" 表中,指定了插入的目標(biāo)表和需要插入的字段。 -
VALUES (#{title}, #{author}, #{readStatus})
: 這也是 SQL 語句的一部分,用來指定插入的具體數(shù)值。#{title}
,#{author}
,#{readStatus}
是 MyBatis 的占位符語法,表示這里會將 Java 對象中的屬性值填充進(jìn)去。#{title}
: 這是占位符,會被實(shí)際的title
屬性值替代。#{author}
: 同樣是占位符,會被實(shí)際的author
屬性值替代。#{readStatus}
: 這也是占位符,會被實(shí)際的readStatus
屬性值替代。
這段 XML 代碼定義了一個插入操作,會將傳入的 title
、author
和 readStatus
屬性值插入到數(shù)據(jù)庫的 "book" 表中的相應(yīng)字段中
5.2 圖書刪除接口
5.2.1 controller.java
public ResponseEntity<Map<String, String>> deleteById(@RequestBody Map<String, Object> requestParams) {
Map<String, Object> data = (Map<String, Object>) requestParams.get("data");
Long id = Long.valueOf(data.get("id").toString());
// 構(gòu)建返回結(jié)果
boolean isDeleted = bookService.deleteById(id);
if (isDeleted) {
Map<String, String> successResponse = new HashMap<>();
successResponse.put("message", "圖書被刪除!");
successResponse.put("status", "success");
return ResponseEntity.ok(successResponse);
} else {
Map<String, String> failResponse = new HashMap<>();
failResponse.put("message", "需要刪除的圖書不存在!");
failResponse.put("status", "fail");
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(failResponse);
}
}
代碼點(diǎn)評:?
這段代碼是一個 Spring Boot 控制器方法,用于處理刪除圖書的請求。下面是代碼的解釋:
-
public ResponseEntity<Map<String, String>> deleteById(@RequestBody Map<String, Object> requestParams) {
: 這是一個控制器方法的聲明。它使用了@RequestBody
注解,表示請求體中的數(shù)據(jù)會被映射為一個Map
對象,并且返回一個ResponseEntity
對象,該對象包含了響應(yīng)數(shù)據(jù)以及 HTTP 狀態(tài)碼等信息。 -
Map<String, Object> data = (Map<String, Object>) requestParams.get("data");
: 這行代碼從請求參數(shù)的data
字段中獲取了一個嵌套的Map
對象,這個嵌套的Map
包含了需要的刪除操作數(shù)據(jù)。 -
Long id = Long.valueOf(data.get("id").toString());
: 這行代碼從嵌套的Map
中獲取了鍵為"id"
的值,并將其轉(zhuǎn)換為Long
類型,這個id
表示要刪除的圖書的唯一標(biāo)識。 -
boolean isDeleted = bookService.deleteById(id);
: 這行代碼調(diào)用了一個bookService
的方法來進(jìn)行實(shí)際的刪除操作,它會嘗試根據(jù)傳入的id
來刪除對應(yīng)的圖書數(shù)據(jù),并返回一個布爾值表示是否刪除成功。 -
if (isDeleted) { ... } else { ... }
: 這是一個條件語句,用于根據(jù)刪除操作的結(jié)果返回不同的響應(yīng)。如果刪除成功,就返回一個成功的響應(yīng),其中包含成功的消息和狀態(tài)。如果刪除失敗,就返回一個失敗的響應(yīng),其中包含失敗的消息和狀態(tài),并且設(shè)置了 HTTP 響應(yīng)狀態(tài)碼為 404(NOT FOUND)。
這段代碼實(shí)現(xiàn)了一個刪除圖書的功能,會從請求體中獲取圖書的唯一標(biāo)識 id
,然后調(diào)用服務(wù)層的方法來刪除圖書,并根據(jù)刪除操作的結(jié)果返回不同的響應(yīng)。
5.2.2 BookService.java
boolean deleteById(Long id);
boolean existsByTitle(@Param("title") String title);
代碼點(diǎn)評:?
當(dāng)需要刪除一本圖書時,可以調(diào)用 boolean deleteById(Long id);
方法。這個方法通常是在一個服務(wù)接口或類中定義的。它使用圖書的唯一標(biāo)識符(id
)來從數(shù)據(jù)庫中刪除對應(yīng)的圖書。方法的工作方式如下:
-
Long id
:這個參數(shù)表示要刪除的圖書的唯一標(biāo)識符。 -
boolean
:方法的返回類型表明刪除是否成功。如果圖書成功刪除,方法返回true
,如果刪除失敗,返回false
。
另一個方法是 boolean existsByTitle(@Param("title") String title);
。這個方法也通常在一個服務(wù)接口或類中定義。它用于檢查數(shù)據(jù)庫中是否存在具有給定標(biāo)題的圖書。方法的工作方式如下:
-
@Param("title") String title
:這個參數(shù)表示要檢查的圖書的標(biāo)題。 -
boolean
:方法的返回類型表明具有給定標(biāo)題的圖書是否存在于數(shù)據(jù)庫中。如果存在,方法返回true
,如果不存在,返回false
。
這兩個方法經(jīng)常結(jié)合使用,以執(zhí)行與在數(shù)據(jù)庫中管理圖書相關(guān)的各種操作。deleteById
方法負(fù)責(zé)根據(jù)圖書的 ID 刪除圖書,而 existsByTitle
方法有助于確定數(shù)據(jù)庫中是否已經(jīng)存在具有特定標(biāo)題的圖書
5.2.3?BookServiceImpl.java
@Override
public boolean deleteById(Long id) {
if (id == null || id <= 0) {
throw new IllegalArgumentException("無效的圖書 ID");
}
int rowsAffected = bookMapper.deleteById(id);
return rowsAffected > 0;
}
代碼點(diǎn)評:?
這段代碼是一個方法實(shí)現(xiàn),用于從數(shù)據(jù)庫中刪除一本圖書。下面是對這段代碼的解釋:
-
@Override
:這個注解表示這個方法是重寫自父類或接口的方法。 -
public boolean deleteById(Long id)
:這是一個公共方法,它的返回類型是布爾值boolean
。它接受一個類型為Long
的參數(shù)id
,表示要刪除的圖書的唯一標(biāo)識符。 -
if (id == null || id <= 0)
:這是一個條件語句,用于檢查傳入的圖書標(biāo)識符是否為無效值。如果id
為null
或小于等于零,就會拋出一個IllegalArgumentException
異常,提示圖書 ID 無效。 -
int rowsAffected = bookMapper.deleteById(id);
:這一行調(diào)用了名為bookMapper
的對象的deleteById
方法,傳入要刪除的圖書的 ID。這個方法將在數(shù)據(jù)庫中執(zhí)行刪除操作,并返回受影響的行數(shù)(即刪除的行數(shù))。 -
return rowsAffected > 0;
:根據(jù)執(zhí)行刪除操作后受影響的行數(shù),這個表達(dá)式返回一個布爾值。如果受影響的行數(shù)大于零,表示刪除成功,返回true
,否則返回false
。這是為了指示刪除操作是否成功完成。
這個方法的作用是接收一個圖書的唯一標(biāo)識符,然后調(diào)用底層的數(shù)據(jù)庫訪問方法,將對應(yīng)的圖書從數(shù)據(jù)庫中刪除。如果刪除成功(受影響的行數(shù)大于零),方法返回 true
,否則返回 false
5.2.4 BookMapper.java
int deleteById(@Param("id") Long id);
代碼點(diǎn)評:?
這段代碼是一個 MyBatis Mapper 接口中的方法定義,用于在數(shù)據(jù)庫中根據(jù)圖書的 ID 刪除記錄。下面是對這段代碼的解釋:
-
int deleteById(@Param("id") Long id);
:這是一個接口方法定義,返回類型是整型int
。它使用了 MyBatis 的@Param
注解,指定了參數(shù)名稱和對應(yīng)的映射關(guān)系。方法接受一個類型為Long
的參數(shù)id
,表示要刪除的圖書的唯一標(biāo)識符。 -
@Param("id")
:這是 MyBatis 的注解,它用于為方法參數(shù)指定一個映射名稱,這里是"id"
。在 SQL 語句中,可以通過#{id}
來引用這個參數(shù)。 -
int
:方法的返回類型是整型int
,表示刪除操作受影響的行數(shù)。
這個方法的作用是為 MyBatis 映射文件提供一個接口,通過調(diào)用這個接口的方法,可以在數(shù)據(jù)庫中執(zhí)行根據(jù)圖書 ID 進(jìn)行刪除操作。在映射文件中,可以通過使用 #{id}
來獲取傳遞的圖書 ID 參數(shù),并執(zhí)行相應(yīng)的刪除 SQL 操作。方法的返回值表示刪除操作后受影響的行數(shù),可以用于判斷刪除是否成功
5.2.5 BookMapper.xml
<!-- 判斷是否存在指定 title 的記錄 -->
<select id="existsByTitle" resultType="boolean" parameterType="java.lang.String">
SELECT COUNT(*) > 0
FROM book
WHERE title = #{title}
</select>
<!-- 刪除 -->
<delete id="deleteById" parameterType="long">
DELETE FROM book WHERE id = #{id}
</delete>
代碼點(diǎn)評:?
這段代碼是 MyBatis 映射文件中的兩個 SQL 查詢語句,分別用于判斷指定標(biāo)題的記錄是否存在以及刪除指定 ID 的記錄。下面是對這兩段代碼的解釋:
-
判斷是否存在指定 title 的記錄:
<select id="existsByTitle" resultType="boolean" parameterType="java.lang.String">
:這是一個查詢語句的定義,通過id
屬性指定查詢的標(biāo)識符,resultType
屬性指定查詢結(jié)果的返回類型,這里是boolean
。parameterType="java.lang.String"
:這個屬性指定了輸入?yún)?shù)的數(shù)據(jù)類型,這里是一個字符串類型的標(biāo)題。SELECT COUNT(*) > 0 FROM book WHERE title = #{title}
:這是查詢的 SQL 語句。它使用了COUNT(*)
來計(jì)算滿足條件的記錄數(shù)是否大于 0,如果大于 0,則說明存在指定標(biāo)題的記錄。其中#{title}
是一個占位符,表示參數(shù)傳遞的標(biāo)題。
-
刪除:
<delete id="deleteById" parameterType="long">
:這是一個刪除語句的定義,通過id
屬性指定刪除的標(biāo)識符,parameterType
屬性指定了傳遞給 SQL 語句的參數(shù)類型,這里是long
類型的 ID。DELETE FROM book WHERE id = #{id}
:這是刪除的 SQL 語句。它會根據(jù)傳遞的id
參數(shù)刪除數(shù)據(jù)庫中對應(yīng)的記錄。其中#{id}
是一個占位符,表示參數(shù)傳遞的圖書 ID。
這兩個 SQL 查詢語句分別用于在數(shù)據(jù)庫中判斷某個標(biāo)題的記錄是否存在(返回一個布爾值)以及根據(jù)圖書 ID 刪除記錄。這些語句會與 MyBatis 中的方法關(guān)聯(lián),通過方法調(diào)用和參數(shù)傳遞來執(zhí)行相應(yīng)的數(shù)據(jù)庫操作
5.3 圖書修改接口
5.3.1 controller.java
@PostMapping("/update")
public ResponseEntity<ResponseData> updateBook(@RequestBody Book book) {
try {
bookService.updateData(book);
ResponseData response = new ResponseData("success", "圖書已更新!");
return ResponseEntity.ok(response);
} catch (IllegalArgumentException e) {
ResponseData response = new ResponseData("fail", e.getMessage());
return ResponseEntity.badRequest().body(response);
} catch (BookNotFoundException e) {
ResponseData response = new ResponseData("fail", "需要修改的書籍id不存在!");
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(response);
}
}
代碼點(diǎn)評:?
這段代碼是一個 Spring Boot 控制器中的方法,用于處理圖書更新的請求。下面是對這段代碼的解釋:
-
@PostMapping("/update")
:這是一個注解,表示這個方法會處理 POST 請求,并且路徑是 "/update"。當(dāng)客戶端發(fā)起一個 POST 請求到 "/update" 路徑時,該方法將被調(diào)用。 -
public ResponseEntity<ResponseData> updateBook(@RequestBody Book book)
:這是方法的簽名,它指定了方法的訪問修飾符、返回值類型以及方法名。@RequestBody
注解表示從請求體中獲取參數(shù),這里是一個Book
對象,即請求中傳遞的要更新的圖書信息。方法返回一個ResponseEntity<ResponseData>
,表示響應(yīng)實(shí)體,其中包含了響應(yīng)的數(shù)據(jù)和狀態(tài)信息。 -
try
:這是一個異常處理的起始塊。在這里,我們嘗試執(zhí)行可能會拋出異常的代碼塊。 -
bookService.updateData(book);
:這是調(diào)用業(yè)務(wù)邏輯層的updateData
方法,傳遞了要更新的圖書信息。這個方法會處理圖書的更新操作。 -
ResponseData response = new ResponseData("success", "圖書已更新!");
:在更新操作成功后,創(chuàng)建一個名為response
的ResponseData
對象,其中包含了成功狀態(tài)和成功的消息。 -
return ResponseEntity.ok(response);
:將上面創(chuàng)建的response
對象作為響應(yīng)體,使用ResponseEntity.ok()
方法構(gòu)建一個成功響應(yīng)實(shí)體,將它返回給客戶端。 -
catch (IllegalArgumentException e)
:這是一個異常捕獲塊,用于捕獲可能拋出的IllegalArgumentException
異常。 -
ResponseData response = new ResponseData("fail", e.getMessage());
:在出現(xiàn)參數(shù)錯誤的情況下,創(chuàng)建一個名為response
的ResponseData
對象,其中包含了失敗狀態(tài)和異常消息。 -
return ResponseEntity.badRequest().body(response);
:將上面創(chuàng)建的response
對象作為響應(yīng)體,使用ResponseEntity.badRequest()
方法構(gòu)建一個錯誤的響應(yīng)實(shí)體,將它返回給客戶端,表示請求參數(shù)錯誤。 -
catch (BookNotFoundException e)
:這是另一個異常捕獲塊,用于捕獲可能拋出的BookNotFoundException
異常。 -
ResponseData response = new ResponseData("fail", "需要修改的書籍id不存在!");
:在找不到需要修改的書籍 ID 的情況下,創(chuàng)建一個名為response
的ResponseData
對象,其中包含了失敗狀態(tài)和消息。 -
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(response);
:將上面創(chuàng)建的response
對象作為響應(yīng)體,使用ResponseEntity.status(HttpStatus.NOT_FOUND)
方法構(gòu)建一個帶有 HTTP 狀態(tài)碼 404 的響應(yīng)實(shí)體,將它返回給客戶端,表示找不到要修改的書籍。
這段代碼實(shí)現(xiàn)了在更新圖書信息時的異常處理和響應(yīng)生成,根據(jù)業(yè)務(wù)邏輯和參數(shù)情況,構(gòu)建不同的響應(yīng)實(shí)體并返回給客戶端
5.3.2 BookService.java
void updateData(Book book);
代碼點(diǎn)評:?
這段代碼是一個服務(wù)類中的方法定義,用于更新圖書信息。下面是對這段代碼的解釋:
-
void
:這是方法的返回類型,表示該方法不返回任何值。 -
updateData
:這是方法的名稱,用于表示這個方法是用來執(zhí)行更新操作的。 -
(Book book)
:這是方法的參數(shù)列表,其中Book
是一個數(shù)據(jù)類型,表示要傳遞的參數(shù)是一個名為book
的Book
對象。這個對象包含了需要更新的圖書的信息。
所以,這段代碼定義了一個名為 updateData
的方法,它接受一個 Book
對象作為參數(shù),該方法的作用是將傳遞進(jìn)來的 Book
對象中的圖書信息用于更新操作。具體的更新操作邏輯將在方法的實(shí)現(xiàn)中進(jìn)行處理
5.3.3?BookServiceImpl.java
@Override
public void updateData(Book book) {
if (book.getId() == null || book.getTitle() == null || book.getAuthor() == null || book.getReadStatus() == null) {
throw new IllegalArgumentException("id、title、author和read_status是必傳參數(shù)!");
}
List<Book> bookList = bookMapper.selectAll();
List<Integer> idList = bookList.stream().map(Book::getId).collect(Collectors.toList());
if (!idList.contains(book.getId())) {
throw new IllegalArgumentException("需要修改的書籍id不存在!");
}
String title = book.getTitle().trim();
String author = book.getAuthor().trim();
int readStatus = book.getReadStatus();
if (title.isEmpty()) {
throw new IllegalArgumentException("title不能為空!");
}
if (author.isEmpty()) {
throw new IllegalArgumentException("作者不能為空!");
}
if (readStatus != 0 && readStatus != 1) {
throw new IllegalArgumentException("閱讀狀態(tài)只能為0和1!");
}
bookMapper.updateData(book.getId().longValue(), title, author, readStatus);
}
代碼點(diǎn)評:?
這段代碼是一個服務(wù)實(shí)現(xiàn)類中的方法,用于更新圖書信息。下面是對這段代碼的解釋:
該方法首先檢查傳遞進(jìn)來的 Book
對象中是否有必要的字段信息(id
、title
、author
和 readStatus
)。然后,獲取數(shù)據(jù)庫中的已存在圖書列表,檢查要修改的圖書的 id
是否存在于列表中。接著,處理圖書的標(biāo)題、作者和閱讀狀態(tài)的信息,進(jìn)行必要的檢查。最后,調(diào)用數(shù)據(jù)訪問層的方法 updateData
進(jìn)行實(shí)際的更新操作,將新的圖書信息應(yīng)用到數(shù)據(jù)庫中。如果任何檢查不通過,將拋出異常
5.3.4 BookMapper.java
void updateData(@Param("id") Long id, @Param("title") String title, @Param("author") String author, @Param("readStatus") Integer readStatus);
代碼點(diǎn)評:?
這段代碼是一個MyBatis Mapper接口中的方法定義,用于更新圖書信息。這里使用了@Param
注解來指定方法參數(shù)與SQL語句中的參數(shù)對應(yīng)關(guān)系。下面是對這段代碼的解釋:
這個方法定義了四個參數(shù),分別是id
、title
、author
和readStatus
。這些參數(shù)用于更新圖書的相關(guān)信息。
-
@Param("id") Long id
: 這個參數(shù)用于指定要更新的圖書的唯一標(biāo)識符(ID)。在SQL語句中,會使用#{id}
來引用這個參數(shù)。 -
@Param("title") String title
: 這個參數(shù)用于指定要更新的圖書的標(biāo)題。在SQL語句中,會使用#{title}
來引用這個參數(shù)。 -
@Param("author") String author
: 這個參數(shù)用于指定要更新的圖書的作者。在SQL語句中,會使用#{author}
來引用這個參數(shù)。 -
@Param("readStatus") Integer readStatus
: 這個參數(shù)用于指定要更新的圖書的閱讀狀態(tài)。在SQL語句中,會使用#{readStatus}
來引用這個參數(shù)。
該方法在調(diào)用時會將這些參數(shù)傳遞給MyBatis框架,框架會根據(jù)參數(shù)的名稱將它們映射到對應(yīng)的SQL語句中。具體的SQL語句是在XML映射文件中定義的,會使用這些參數(shù)來執(zhí)行更新操作,將新的圖書信息應(yīng)用到數(shù)據(jù)庫中
5.3.5 BookMapper.xml
<!-- 修改 -->
<update id="updateData">
UPDATE book SET
title = #{title},
author = #{author},
read_status = #{readStatus}
WHERE id = #{id}
</update>
代碼點(diǎn)評:?
這段XML代碼是一個MyBatis的映射文件中的一個SQL語句定義,用于更新圖書信息。下面是對這段代碼的解釋:
這個<update>
元素定義了一個更新操作的SQL語句。它使用了MyBatis的占位符語法(#{}
)來引用參數(shù),具體含義如下:
-
UPDATE book SET
: 這是SQL語句的更新部分,表示要對book
表進(jìn)行更新操作。 -
title = #{title},
: 這里使用了占位符語法#{title}
,表示要將title
參數(shù)的值賦給數(shù)據(jù)庫中的title
列。 -
author = #{author},
: 類似地,將author
參數(shù)的值賦給數(shù)據(jù)庫中的author
列。 -
read_status = #{readStatus}
: 同樣,將readStatus
參數(shù)的值賦給數(shù)據(jù)庫中的read_status
列。 -
WHERE id = #{id}
: 這個部分指定了更新的條件,只會對id
等于#{id}
的記錄進(jìn)行更新。
在使用這個SQL語句時,MyBatis會將實(shí)際的參數(shù)值替換到占位符#{}
中,然后執(zhí)行這個更新操作。這樣就能根據(jù)傳遞的參數(shù)更新圖書的標(biāo)題、作者和閱讀狀態(tài)
5.4 圖書查詢接口
5.4.1 controller.java
@PostMapping("/query")
public ResponseData<List<Book>> queryBookByTitle(@RequestBody Map<String, Object> requestParams) {
Map<String, Object> data = (Map<String, Object>) requestParams.get("data");
String title = (String) data.get("title");
int pagesize = data.get("pagesize") != null && !data.get("pagesize").toString().isEmpty() ? Integer.parseInt(data.get("pagesize").toString()) : 3;
int page = data.get("page") != null && !data.get("page").toString().isEmpty() ? Integer.parseInt(data.get("page").toString()) : 1;
List<Book> result = bookService.queryBookByTitle(title, pagesize, page);
if (result.isEmpty()) {
return new ResponseData<>("需要查詢的圖書不存在!", "fail", result);
}
return new ResponseData<>("請求成功", "success", result);
}
代碼點(diǎn)評:?
這段代碼是一個控制器(Controller)中的一個方法,用于處理通過標(biāo)題查詢圖書的請求。以下是代碼的解釋:
-
@PostMapping("/query")
: 這是一個用于處理 HTTP POST 請求的注解,指定了請求的路徑為 "/query"。 -
public ResponseData<List<Book>> queryBookByTitle(@RequestBody Map<String, Object> requestParams)
: 這是一個方法定義,它接受一個 Map 對象作為請求體,并返回一個ResponseData
類型的對象,表示響應(yīng)數(shù)據(jù)。 -
Map<String, Object> data = (Map<String, Object>) requestParams.get("data");
: 從傳入的請求體中獲取名為 "data" 的字段,它應(yīng)該是一個包含請求參數(shù)的 Map 對象。 -
String title = (String) data.get("title");
: 從獲取到的數(shù)據(jù)中獲取 "title" 字段,這是用于查詢的圖書標(biāo)題。 -
int pagesize = ...
: 獲取 "pagesize" 和 "page" 字段,這些字段指定了分頁的大小和頁碼,默認(rèn)為 3 和 1。 -
List<Book> result = bookService.queryBookByTitle(title, pagesize, page);
: 使用獲取到的標(biāo)題、分頁大小和頁碼調(diào)用業(yè)務(wù)邏輯層的方法來查詢圖書數(shù)據(jù)。 -
if (result.isEmpty()) { ... }
: 檢查查詢結(jié)果是否為空。如果為空,創(chuàng)建一個新的ResponseData
對象,其中包含 "需要查詢的圖書不存在!" 的消息和 "fail" 的狀態(tài)。 -
return new ResponseData<>("請求成功", "success", result);
: 如果查詢結(jié)果不為空,創(chuàng)建一個新的ResponseData
對象,其中包含 "請求成功" 的消息和 "success" 的狀態(tài),同時將查詢結(jié)果作為數(shù)據(jù)。
小結(jié):這個方法通過標(biāo)題查詢圖書,如果查詢結(jié)果為空,則返回一個包含錯誤消息和 "fail" 狀態(tài)的響應(yīng)對象;如果查詢結(jié)果不為空,則返回一個包含成功消息和 "success" 狀態(tài)以及查詢結(jié)果數(shù)據(jù)的響應(yīng)對象
5.4.2?BookService.java
List<Book> queryBookByTitle(String title, Integer pagesize, Integer page);
代碼點(diǎn)評:?
這段代碼是服務(wù)層(Service Layer)接口中的一個方法聲明,用于根據(jù)書名、每頁條數(shù)和頁碼來查詢圖書信息。下面是對這段代碼的解釋:
這個方法聲明了一個名為 queryBookByTitle
的方法,它接受三個參數(shù):
-
title
:表示要查詢的書名。這是一個字符串類型的參數(shù),用于指定要查詢的書名關(guān)鍵字。 -
pagesize
:表示每頁顯示的條數(shù)。這是一個整數(shù)類型的參數(shù),用于指定每頁要顯示的圖書條目數(shù)。 -
page
:表示要查詢的頁碼。這是一個整數(shù)類型的參數(shù),用于指定要查詢的頁碼編號。
方法聲明的返回類型是一個 List<Book>
,這表示該方法將會返回一個由 Book
對象組成的列表,這些 Book
對象是根據(jù)查詢條件獲取到的圖書信息。
這個方法的作用是在服務(wù)層中根據(jù)給定的書名、每頁條數(shù)和頁碼來執(zhí)行圖書查詢操作,然后返回查詢結(jié)果。通常,該方法會調(diào)用底層的數(shù)據(jù)庫查詢方法,獲取滿足條件的圖書數(shù)據(jù),并將結(jié)果轉(zhuǎn)化為 Book
對象列表返回給調(diào)用方
5.4.3?BookServiceImpl.java
@Override
public List<Book> queryBookByTitle(String title, Integer pagesize, Integer page) {
int offset = pagesize * (page - 1);
RowBounds rowBounds = new RowBounds(offset, pagesize);
return bookMapper.queryBookByTitle(title, rowBounds);
}
代碼點(diǎn)評:?
這段代碼是服務(wù)層(Service Layer)中的方法實(shí)現(xiàn),用于根據(jù)書名、每頁條數(shù)和頁碼來查詢圖書信息。下面是對這段代碼的解釋:
這個方法實(shí)現(xiàn)了先前聲明的 queryBookByTitle
方法。它接受三個參數(shù):
title
:要查詢的書名關(guān)鍵字。pagesize
:每頁顯示的圖書條目數(shù)。page
:要查詢的頁碼。
在方法中,首先計(jì)算出數(shù)據(jù)的偏移量(offset),這個偏移量是通過頁碼和每頁條數(shù)計(jì)算得到的,用于確定從數(shù)據(jù)庫中哪條記錄開始獲取數(shù)據(jù)。
然后,創(chuàng)建了一個 RowBounds
對象,這個對象用于在數(shù)據(jù)庫查詢中指定偏移量和限制的條目數(shù),實(shí)現(xiàn)分頁查詢。
最后,調(diào)用了 bookMapper.queryBookByTitle
方法,將書名關(guān)鍵字和分頁信息傳遞給數(shù)據(jù)庫查詢方法。查詢結(jié)果會根據(jù)傳入的分頁信息獲取相應(yīng)的圖書數(shù)據(jù),并將查詢結(jié)果以 List<Book>
的形式返回給調(diào)用方。
總之,這個方法的作用是在服務(wù)層中使用 MyBatis 的分頁功能,根據(jù)給定的書名關(guān)鍵字、每頁條數(shù)和頁碼,執(zhí)行圖書查詢操作,并返回查詢結(jié)果
5.4.4 BookMapper.java
List<Book> queryBookByTitle(@Param("title") String title, RowBounds rowBounds);
List<Book> selectAll();
代碼點(diǎn)評:?
這兩個方法是 MyBatis 的 Mapper 接口中定義的查詢方法,用于在數(shù)據(jù)庫中進(jìn)行數(shù)據(jù)查詢操作。下面分別解釋這兩個方法:
-
List<Book> queryBookByTitle(@Param("title") String title, RowBounds rowBounds);
這個方法用于根據(jù)書名關(guān)鍵字和分頁信息來查詢圖書列表。下面是對方法的各部分進(jìn)行解釋:
@Param("title") String title
:這個注解是用于為方法參數(shù)指定名稱的 MyBatis 注解。在查詢語句中,可以通過該名稱引用這個參數(shù)。在這個方法中,它用于指定查詢圖書時要匹配的書名關(guān)鍵字。RowBounds rowBounds
:這是 MyBatis 提供的一個分頁信息對象,它包含偏移量和限制條數(shù)。通過傳遞這個對象,可以在查詢中實(shí)現(xiàn)分頁功能。
-
List<Book> selectAll();
這個方法用于查詢所有圖書的信息,返回一個包含所有圖書對象的列表。它沒有參數(shù),因?yàn)樗且粋€簡單的查詢操作,不需要額外的查詢條件。
總之,這兩個方法分別用于根據(jù)書名關(guān)鍵字和分頁信息查詢圖書列表,以及查詢所有圖書的信息。它們都是在 MyBatis 的 Mapper 接口中定義的,實(shí)際的查詢操作由 MyBatis 框架負(fù)責(zé)執(zhí)行
5.4.5 BookMapper.xml
<!-- 查詢 -->
<select id="queryBookByTitle" resultType="com.example.book.domain.Book">
SELECT id,title,author,read_status readStatus FROM book
<where>
<if test="title != null and title != ''">
AND title LIKE CONCAT('%', #{title}, '%')
</if>
</where>
</select>
<!-- 查詢所有圖書 -->
<select id="selectAll" resultType="com.example.book.domain.Book">
SELECT id, title, author, read_status as readStatus
FROM book
代碼點(diǎn)評:?
這兩段代碼是 MyBatis 的映射文件中的查詢語句,用于從數(shù)據(jù)庫中獲取圖書信息。以下是對每個查詢語句的解釋:
? ? ? ? ?1、查詢圖書根據(jù)書名關(guān)鍵字:
<select>
標(biāo)簽:定義一個查詢語句。id="queryBookByTitle"
:為查詢語句指定一個唯一的標(biāo)識符。resultType="com.example.book.domain.Book"
:指定查詢結(jié)果的類型,即圖書對象的類型。SELECT id, title, author, read_status readStatus FROM book
:實(shí)際的 SQL 查詢語句,從數(shù)據(jù)庫中選擇圖書的 id、title、author 和 read_status 字段。<where>
標(biāo)簽:這個標(biāo)簽用于生成 SQL 的 WHERE 子句,根據(jù)條件動態(tài)生成。<if test="title != null and title != ''">
:條件判斷語句,判斷是否傳入了 title 參數(shù)并且不為空。AND title LIKE CONCAT('%', #{title}, '%')
:如果滿足條件,會在 SQL 查詢語句中添加類似AND title LIKE '%關(guān)鍵字%'
的條件,進(jìn)行模糊匹配書名關(guān)鍵字。? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?2、查詢所有圖書?-
<select>
標(biāo)簽:同樣定義一個查詢語句。id="selectAll"
:為查詢語句指定唯一標(biāo)識符。resultType="com.example.book.domain.Book"
:指定查詢結(jié)果的類型,即圖書對象的類型。SELECT id, title, author, read_status as readStatus FROM book
:從數(shù)據(jù)庫中選擇圖書的 id、title、author 和 read_status 字段。
-
這兩個查詢語句定義了如何從數(shù)據(jù)庫中獲取圖書信息,通過 MyBatis 框架進(jìn)行執(zhí)行。第一個查詢語句允許根據(jù)書名關(guān)鍵字進(jìn)行模糊查詢,第二個查詢語句獲取所有圖書的信息
六、增刪改查接口測試
CRUD,增刪改查接口已經(jīng)開發(fā)完畢,下面我們開始測試。注意,這里只列了部分場景。入?yún)⒁沧隽诵r?yàn),這里就不列截圖了
6.1 圖書增加測試
6.1.1? 添加圖書測試
請求示例:
{
"title":"《Spring高級編程》",
"author":"李專家",
"read_status":1
}
響應(yīng)示例:?
{
"message": "圖書添加成功!",
"status": "success"
}
完整截圖:
數(shù)據(jù)庫驗(yàn)證:
6.1.2??添加重復(fù)圖書測試
再次點(diǎn)擊,報(bào)錯:書名重復(fù)
6.1.3??圖書添加接口測試結(jié)論
?新增接口測試通過
6.2 圖書刪除測試
6.2.1 通過id刪除圖書測試
準(zhǔn)備刪除id為 67的書籍
請求示例:
{
"data": {
"id": "67"
},
"extra": {}
}
響應(yīng)示例:?
{
"message": "圖書被刪除!",
"status": "success"
}
完整截圖:
數(shù)據(jù)庫驗(yàn)證:
?6.2.2?通過id刪除不存在圖書測試
再次點(diǎn)擊,報(bào)錯:圖書不存在
?6.2.3 圖書刪除接口測試結(jié)論?
?刪除接口測試通過
6.3 圖書修改測試
6.3.1 通過修改圖書標(biāo)題測試
修改id 為68的書籍?
請求示例:
{
"id": 68,
"title": "《我是測試開發(fā)》",
"author": "李同學(xué)",
"read_status": "0"
}
響應(yīng)示例:?
data是拓展字段,這里返回null沒影響。前端頁面暫時不用
{
"message": "success",
"status": "圖書已更新!",
"data": null
}
完整截圖:
數(shù)據(jù)庫驗(yàn)證:
6.3.2 通過修改圖書作者測試
再次修改作業(yè) 天下霸唱,點(diǎn)擊修改返回成功
?查看數(shù)據(jù)庫,修改成功
6.3.3 圖書修改接口測試結(jié)論
修改接口測試通過
6.4 圖書查詢測試
6.4.1 不傳書名,頁數(shù)大小10,第1頁測試
請求示例:
{
"data": {
"title": "",
"pagesize": "10",
"page": "1"
},
"extra": {}
}
響應(yīng)示例:
{
"message": "請求成功",
"status": "success",
"data": [
{
"id": 68,
"title": "《我是測試》",
"author": "李同學(xué)",
"read_status": 0
},
{
"id": 69,
"title": "《三國演義20》",
"author": "羅貫中",
"read_status": 1
},
{
"id": 70,
"title": "《默》",
"author": "李曉明",
"read_status": 0
},
{
"id": 71,
"title": "《三國演義8》",
"author": "羅貫中8",
"read_status": 1
},
{
"id": 72,
"title": "《Java高級編程》",
"author": "李專家",
"read_status": 1
},
{
"id": 73,
"title": "《Spring高級編程》",
"author": "李專家",
"read_status": 1
}
]
}
完整截圖:
數(shù)據(jù)庫驗(yàn)證:
6.4.2 關(guān)鍵字過濾查詢測試
查詢過濾:
{
"data": {
"title": "編程",
"pagesize": "10",
"page": "1"
},
"extra": {}
}
結(jié)果:
{
"message": "請求成功",
"status": "success",
"data": [
{
"id": 72,
"title": "《Java高級編程》",
"author": "李專家",
"read_status": 1
},
{
"id": 73,
"title": "《Spring高級編程》",
"author": "李專家",
"read_status": 1
}
]
}
?查詢成功
6.4.3?所有值不傳分頁默認(rèn)傳3和1測試
分頁查詢:默認(rèn)傳3和1,title不傳查所有
{
"data": {
"title": "",
"pagesize": "",
"page": ""
},
"extra": {}
}
響應(yīng):
{
"message": "請求成功",
"status": "success",
"data": [
{
"id": 68,
"title": "《我是測試》",
"author": "李同學(xué)",
"read_status": 0
},
{
"id": 69,
"title": "《三國演義20》",
"author": "羅貫中",
"read_status": 1
},
{
"id": 70,
"title": "《默》",
"author": "李曉明",
"read_status": 0
}
]
}
?對照數(shù)據(jù)庫
?6.4.4?圖書不存在時查詢測試
查詢不存在的圖書時:
?請求:
{
"data": {
"title": "粑粑",
"pagesize": "",
"page": ""
},
"extra": {}
}
響應(yīng):
{
"message": "需要查詢的圖書不存在!",
"status": "fail",
"data": []
}
?截圖:
?數(shù)據(jù)庫里沒有這個標(biāo)題相關(guān)的書籍:
標(biāo)題不存在圖書測試通過?
?6.4.5?圖書查詢接口測試結(jié)論
查詢接口測試通過
七、總結(jié)
本次包括接口開發(fā)、測試、博文撰寫斷斷續(xù)續(xù)寫了幾天,總體上比之前flask實(shí)現(xiàn)的要好一些。目前這種CRUD基本上可以在項(xiàng)目上使用了,優(yōu)化點(diǎn)是可以加上更多的校驗(yàn)和更多的封裝,每段代碼的解釋也是非常的詳細(xì),非常適合學(xué)習(xí)
八、期望
作為一名測試專家。Java是必須掌握的語言,而掌握java必須得有幾個框架的項(xiàng)目實(shí)戰(zhàn),這樣才能做好測試。擁有和開發(fā)同頻對話能力,增加測試自信心,當(dāng)然也是能力的體現(xiàn)。后續(xù)會根據(jù)項(xiàng)目分享更多的白盒測試、單元測試文章,共同進(jìn)步,不說了,老婆叫我包餃子了。。。
九、源碼
本來要放在git上的,這一次先全部貼出來。在公司電腦操作涉及安全,我還是先保住工作哈哈哈
9.1 DataSourceConfig.java
//這個文件是禁用了Spring Boot默認(rèn)的數(shù)據(jù)源自動配置 ,
// 導(dǎo)致報(bào)錯"Consider defining a bean of type 'javax.sql.DataSource' in your configuration"
//加了這個文件也能處理這個報(bào)錯讓程序啟動
//package com.example.book.config;
//import org.springframework.boot.jdbc.DataSourceBuilder;
//import org.springframework.context.annotation.Bean;
//import org.springframework.context.annotation.Configuration;
//import javax.sql.DataSource;
//
//@Configuration
//public class DataSourceConfig {
// @Bean
// public DataSource dataSource() {
// return DataSourceBuilder.create()
// .url("jdbc:mysql://XX.XX.XXX.24:13300/z_liqiju_test?tinyInt1isBit=false&useUnicode=true&characterEncoding=utf-8&autoReconnect=true&failOverReadOnly=false&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=Asia/Shanghai&allowMultiQueries=true\n")
// .username("XXX")
// .password("XXX")
// .driverClassName("com.mysql.cj.jdbc.Driver")
// .build();
// }
//}
9.2 MyBatisConfig.java
//package com.example.book.config;
//這個文件是解決報(bào)錯Consider defining a bean of type 'org.apache.ibatis.session.SqlSessionFactory' in your configuration.
//這個config包的2個文件是我在啟動類那里加了1行@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
//禁用了Spring Boot默認(rèn)的數(shù)據(jù)源自動配置 ,導(dǎo)致報(bào)錯"Consider defining a bean of type 'javax.sql.DataSource' in your configuration"。坑死個人
//import org.apache.ibatis.session.SqlSessionFactory;
//import org.mybatis.spring.SqlSessionFactoryBean;
//import org.mybatis.spring.annotation.MapperScan;
//import org.springframework.context.annotation.Bean;
//import org.springframework.context.annotation.Configuration;
//import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
//import org.springframework.core.io.support.ResourcePatternResolver;
//import javax.sql.DataSource;
//
//@Configuration
//@MapperScan("com.example.book.mapper") // Mapper接口所在的包路徑
//public class MyBatisConfig {
// private final DataSource dataSource;
//
// public MyBatisConfig(DataSource dataSource) {
// this.dataSource = dataSource;
// }
//
// @Bean
// public SqlSessionFactory sqlSessionFactory() throws Exception {
// SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
// factoryBean.setDataSource(dataSource);
//
// // 指定MyBatis的Mapper XML文件所在的路徑
// ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
// factoryBean.setMapperLocations(resolver.getResources("classpath:mapper/*.xml"));
//
// return factoryBean.getObject();
// }
//}
9.3 controller.java
package com.example.book.controller;
import com.example.book.domain.Book;
import com.example.book.domain.ResponseData;
import com.example.book.service.BookService;
import com.example.book.service.impl.BookNotFoundException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/api/book")
public class BookController {
private static final Logger logger = LoggerFactory.getLogger(BookController.class);
private final BookService bookService;
@Autowired
public BookController(BookService bookService){
this.bookService = bookService;
}
@PostMapping("/add")
public ResponseEntity<Map<String, String>> addBook(@RequestBody Book book) {
try {
//System.out.println("前端傳值ReadStatus:"+book.getReadStatus());
bookService.insertData(book);
Map<String, String> response = new HashMap<>();
response.put("message", "圖書添加成功!");
response.put("status", "success");
return ResponseEntity.ok(response);
} catch (IllegalArgumentException e) {
Map<String, String> response = new HashMap<>();
response.put("message", e.getMessage());
response.put("status", "fail");
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(response);
}
}
@PostMapping("/delete")
public ResponseEntity<Map<String, String>> deleteById(@RequestBody Map<String, Object> requestParams) {
Map<String, Object> data = (Map<String, Object>) requestParams.get("data");
Long id = Long.valueOf(data.get("id").toString());
// 構(gòu)建返回結(jié)果
boolean isDeleted = bookService.deleteById(id);
if (isDeleted) {
Map<String, String> successResponse = new HashMap<>();
successResponse.put("message", "圖書被刪除!");
successResponse.put("status", "success");
return ResponseEntity.ok(successResponse);
} else {
Map<String, String> failResponse = new HashMap<>();
failResponse.put("message", "需要刪除的圖書不存在!");
failResponse.put("status", "fail");
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(failResponse);
}
}
// @PostMapping("/update")
// public void updateBook(@PathVariable Long id, @RequestBody Book book) {
// bookService.updateData(id, book.getTitle(), book.getAuthor(), book.getReadStatus());
// }
@PostMapping("/update")
public ResponseEntity<ResponseData> updateBook(@RequestBody Book book) {
try {
bookService.updateData(book);
ResponseData response = new ResponseData("success", "圖書已更新!");
return ResponseEntity.ok(response);
} catch (IllegalArgumentException e) {
ResponseData response = new ResponseData("fail", e.getMessage());
return ResponseEntity.badRequest().body(response);
} catch (BookNotFoundException e) {
ResponseData response = new ResponseData("fail", "需要修改的書籍id不存在!");
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(response);
}
}
@PostMapping("/query")
public ResponseData<List<Book>> queryBookByTitle(@RequestBody Map<String, Object> requestParams) {
Map<String, Object> data = (Map<String, Object>) requestParams.get("data");
String title = (String) data.get("title");
int pagesize = data.get("pagesize") != null && !data.get("pagesize").toString().isEmpty() ? Integer.parseInt(data.get("pagesize").toString()) : 3;
int page = data.get("page") != null && !data.get("page").toString().isEmpty() ? Integer.parseInt(data.get("page").toString()) : 1;
List<Book> result = bookService.queryBookByTitle(title, pagesize, page);
if (result.isEmpty()) {
return new ResponseData<>("需要查詢的圖書不存在!", "fail", result);
}
return new ResponseData<>("請求成功", "success", result);
}
}
9.4 BaseInput.java
//package com.example.book.domain;
//
//import io.swagger.annotations.ApiModelProperty;
//import lombok.Data;
//
//import java.util.Map;
//封裝請求
//@Data
//public class BaseInput<T> {
//
// @ApiModelProperty(value = "實(shí)際請求參數(shù)數(shù)據(jù)")
// private T data;
//
// @ApiModelProperty(value = "擴(kuò)展數(shù)據(jù),字典類型key-value結(jié)構(gòu)")
// private Map<String,Object> extra;
//}
9.5 Book.java
package com.example.book.domain;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
//@ApiModelProperty干嘛的?
//@ApiModelProperty 是 Swagger 注解之一,用于在 API 文檔中描述接口的各個字段(屬性)的用途、數(shù)據(jù)類型、示例值等信息,以便生成更詳細(xì)的 API 文檔。
//Swagger 是一個用于生成和展示 API 文檔的工具,可以幫助開發(fā)人員更好地了解和使用 API。
//具體來說,@ApiModelProperty 注解可以用于類的屬性上,用于描述 API 接口的請求或響應(yīng)對象的字段。這些描述將會被 Swagger 生成的 API 文檔所使用。主要用途包括:
//描述字段用途: 通過 @ApiModelProperty 注解,你可以為每個字段添加描述,解釋該字段的用途、作用等,使文檔更加易讀和易理解。
//指定數(shù)據(jù)類型: 你可以通過 dataType 參數(shù)來指定字段的數(shù)據(jù)類型,例如字符串、整數(shù)、浮點(diǎn)數(shù)等。
//提供示例值: 使用 example 參數(shù)可以為字段提供示例值,幫助使用者更好地理解字段的期望值。
//控制是否必填: 通過 required 參數(shù),你可以指定字段是否為必填項(xiàng)。
//其他屬性: 還可以設(shè)置一些其他屬性,如是否允許空值、是否隱藏該字段等。
@TableName(value ="books")
@Data
public class Book implements Serializable {
/**
* 自動遞增id,唯一鍵
*/
@TableId(type = IdType.AUTO)
private Integer id;
@ApiModelProperty(value = "書名")
private String title;
@ApiModelProperty(value = "作者")
private String author;
@ApiModelProperty(value = "閱讀狀態(tài)")
@JsonProperty("read_status") // 指定 JSON 字段名,使得前端傳參和read_status對應(yīng)readStatus
private Integer readStatus;
@TableField(exist = false)
private static final long serialVersionUID = 1L;
//構(gòu)造函數(shù)
public Book(Integer id, String title, String author, Integer readStatus) {
this.id = id;
this.title = title;
this.author = author;
this.readStatus = readStatus;
}
}
9.6 ResponseData.java
package com.example.book.domain;
import lombok.Data;
//ResponseData是一個用于封裝響應(yīng)數(shù)據(jù)的泛型類。
//它用于統(tǒng)一返回給前端的響應(yīng)格式,包含三個屬性:message、status和data。
//屬性說明:
//message:表示響應(yīng)的消息或描述信息,通常用于描述請求的處理結(jié)果或返回的狀態(tài)信息。
//status:表示響應(yīng)的狀態(tài),通常用于標(biāo)識請求處理的狀態(tài),例如"success"表示成功,"error"表示失敗等。
//data:表示響應(yīng)的數(shù)據(jù),它是一個泛型參數(shù),可以用于存儲任意類型的數(shù)據(jù),比如查詢結(jié)果、實(shí)體對象等。
//例如,在查詢數(shù)據(jù)庫時,可以將查詢結(jié)果存儲在data中返回給前端;在處理表單提交時,可以將表單提交的數(shù)據(jù)存儲在data中返回。
//構(gòu)造函數(shù):
//ResponseData(String message, String status, T data):該構(gòu)造函數(shù)用于創(chuàng)建ResponseData對象,
//并初始化message、status和data屬性的值。
//@Data是一個Lombok注解,它可以自動為類生成一些常用的方法,如toString()、equals()、hashCode()、getter和setter方法。使用@Data注解可以簡化Java類的編寫,減少樣板代碼。
//具體來說,@Data注解為類的所有非靜態(tài)字段生成以下方法:
//toString(): 生成一個默認(rèn)的toString()方法,用于將對象轉(zhuǎn)換為字符串表示。該方法會按照字段的名稱和值生成字符串。
//equals(): 生成一個默認(rèn)的equals()方法,用于比較對象是否相等。該方法會比較對象的所有字段是否相等。
//hashCode(): 生成一個默認(rèn)的hashCode()方法,用于計(jì)算對象的哈希碼。該方法基于對象的所有字段計(jì)算哈希碼。
//getter和setter: 為所有非靜態(tài)字段生成getter和setter方法,用于獲取和設(shè)置字段的值。
//使用@Data注解的類通常稱為"數(shù)據(jù)類",它主要用于封裝數(shù)據(jù),而不包含業(yè)務(wù)邏輯。在編寫POJO(Plain Old Java Object)類時,使用@Data注解可以簡化代碼,提高代碼的可讀性和可維護(hù)性。
@Data
public class ResponseData<T> {
private String message;
private String status;
private T data;
public ResponseData(String message, String status, T data) {
this.message = message;
this.status = status;
this.data = data;
}
public ResponseData(String message, String status) {
this.message = message;
this.status = status;
}
}
9.7 BookService.java
package com.example.book.service;
import com.example.book.domain.Book;
import java.util.List;
/**
*
*/
public interface BookService {
void insertData(Book book);
boolean deleteById(Long id);
;
void updateData(Book book);
List<Book> queryBookByTitle(String title, Integer pagesize, Integer page);
}
9.8?BookServiceImpl.java
package com.example.book.service.impl;
import com.example.book.domain.Book;
import com.example.book.mapper.BookMapper;
import com.example.book.service.BookService;
import org.apache.ibatis.session.RowBounds;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.stream.Collectors;
//創(chuàng)建了BooksServiceImpl類,并將其標(biāo)記為@Service,以便Spring能夠識別并注冊它作為bean。
//同時,也要確保在BooksServiceImpl的構(gòu)造函數(shù)中使用了@Autowired來注入BooksMapper。
//在MyBatis中,分頁應(yīng)該使用RowBounds或者使用PageHelper來實(shí)現(xiàn)
@Service
public class BookServiceImpl implements BookService {
private final BookMapper bookMapper;
@Autowired
public BookServiceImpl(BookMapper booksMapper) {
this.bookMapper = booksMapper;
}
@Override
public void insertData(Book book) {
if (book.getTitle() == null || book.getAuthor() == null || book.getReadStatus() == null) {
throw new IllegalArgumentException("title、author和read_status是必傳參數(shù)!");
}
// Check for duplicate title
String title = book.getTitle().trim();
if (bookMapper.existsByTitle(title)) {
throw new IllegalArgumentException("書名(title)重復(fù)!");
}
bookMapper.insertData(book);
}
@Override
public boolean deleteById(Long id) {
if (id == null || id <= 0) {
throw new IllegalArgumentException("無效的圖書 ID");
}
int rowsAffected = bookMapper.deleteById(id);
return rowsAffected > 0;
}
// @Override
// public void updateData(Long id, String title, String author, Integer readStatus) {
// bookMapper.updateData(id, title, author, readStatus);
// }
@Override
public void updateData(Book book) {
if (book.getId() == null || book.getTitle() == null || book.getAuthor() == null || book.getReadStatus() == null) {
throw new IllegalArgumentException("id、title、author和read_status是必傳參數(shù)!");
}
List<Book> bookList = bookMapper.selectAll();
List<Integer> idList = bookList.stream().map(Book::getId).collect(Collectors.toList());
if (!idList.contains(book.getId())) {
throw new IllegalArgumentException("需要修改的書籍id不存在!");
}
String title = book.getTitle().trim();
String author = book.getAuthor().trim();
int readStatus = book.getReadStatus();
if (title.isEmpty()) {
throw new IllegalArgumentException("title不能為空!");
}
if (author.isEmpty()) {
throw new IllegalArgumentException("作者不能為空!");
}
if (readStatus != 0 && readStatus != 1) {
throw new IllegalArgumentException("閱讀狀態(tài)只能為0和1!");
}
bookMapper.updateData(book.getId().longValue(), title, author, readStatus);
}
@Override
public List<Book> queryBookByTitle(String title, Integer pagesize, Integer page) {
int offset = pagesize * (page - 1);
RowBounds rowBounds = new RowBounds(offset, pagesize);
return bookMapper.queryBookByTitle(title, rowBounds);
}
}
9.9?BookNotFoundException.java
package com.example.book.service.impl;
public class BookNotFoundException extends RuntimeException {
public BookNotFoundException(String message) {
super(message);
}
}
9.10 BookMapper.java
package com.example.book.mapper;
import com.example.book.domain.Book;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.session.RowBounds;
import java.util.List;
/**
* @Entity com.example.book.domain.Books
*/
@Mapper
public interface BookMapper extends BaseMapper<Book> {
void insertData(Book book);
boolean existsByTitle(@Param("title") String title);
int deleteById(@Param("id") Long id);
void updateData(@Param("id") Long id, @Param("title") String title, @Param("author") String author, @Param("readStatus") Integer readStatus);
List<Book> queryBookByTitle(@Param("title") String title, RowBounds rowBounds);
List<Book> selectAll();
}
9.11 BookApplication.java
package com.example.book;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("com.example.book.mapper")
public class BookApplication {
public static void main(String[] args) {
SpringApplication.run(BookApplication.class, args);
}
}
9.12 BookMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.book.mapper.BookMapper">
<resultMap id="BaseResultMap" type="com.example.book.domain.Book">
<id property="id" column="id" jdbcType="INTEGER"/>
<result property="title" column="title" jdbcType="VARCHAR"/>
<result property="author" column="author" jdbcType="VARCHAR"/>
<result property="readStatus" column="read_status" jdbcType="INTEGER"/>
</resultMap>
<sql id="Base_Column_List">
id,title,author,
read_status
</sql>
<!-- 增加 -->
<insert id="insertData">
INSERT INTO book (title, author, read_status)
VALUES (#{title}, #{author}, #{readStatus})
</insert>
<!-- 判斷是否存在指定 title 的記錄 -->
<select id="existsByTitle" resultType="boolean" parameterType="java.lang.String">
SELECT COUNT(*) > 0
FROM book
WHERE title = #{title}
</select>
<!-- 刪除 -->
<delete id="deleteById" parameterType="long">
DELETE FROM book WHERE id = #{id}
</delete>
<!-- 修改 -->
<update id="updateData">
UPDATE book SET
title = #{title},
author = #{author},
read_status = #{readStatus}
WHERE id = #{id}
</update>
<!-- 查詢 -->
<select id="queryBookByTitle" resultType="com.example.book.domain.Book">
SELECT id,title,author,read_status readStatus FROM book
<where>
<if test="title != null and title != ''">
AND title LIKE CONCAT('%', #{title}, '%')
</if>
</where>
</select>
<!-- 查詢所有圖書 -->
<select id="selectAll" resultType="com.example.book.domain.Book">
SELECT id, title, author, read_status as readStatus
FROM books
</select>
</mapper>
9.13 application.properties
注意我加了個1在后綴,2個文件選擇1個生效即可
spring.datasource.url = jdbc:mysql://XX.XX.XXX.24:13300/z_liqiju_test?tinyInt1isBit=false&useUnicode=true&characterEncoding=utf-8&autoReconnect=true&failOverReadOnly=false&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=Asia/Shanghai&allowMultiQueries=true
spring.datasource.username = XXX
spring.datasource.password = XXX
mybatis.mapper-locations = classpath*:mapper/*.xml
server.port = 5002
logging.level.root=info
#logging.level.root=DEBUG
#logging.level.org.mybatis=DEBUG
9.14 application.yml
原來是的項(xiàng)目是5001 端口,這一次改為5002端口啟動防止端口沖突
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://XX.XX.XXX.24:13300/z_liqiju_test?tinyInt1isBit=false&useUnicode=true&characterEncoding=utf-8&autoReconnect=true&failOverReadOnly=false&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=Asia/Shanghai&allowMultiQueries=true
username: XXX
password: XXX
mybatis:
mapper-locations: classpath*:mapper/*.xml
server:
port: 5002
logging:
level:
root: DEBUG
# org:
# mybatis: INFO
9.15 pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.0.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>book</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>book</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!-- spring-boot-starter-jdbc 驅(qū)動 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!-- spring-boot-starter-web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-annotation</artifactId>
<version>3.3.1</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-core</artifactId>
<version>3.3.1</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-extension</artifactId>
<version>3.3.1</version>
</dependency>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-annotations</artifactId>
<version>1.6.6</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.2.5.RELEASE</version>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
<configuration>
<skipTests>true</skipTests>
</configuration>
</plugin>
</plugins>
</build>
</project>
2023年8月12日 -深圳筆文章來源:http://www.zghlxwxcb.cn/news/detail-771247.html
有疑問直接評論區(qū)見文章來源地址http://www.zghlxwxcb.cn/news/detail-771247.html
到了這里,關(guān)于Spring Boot+Mybatis實(shí)現(xiàn)增刪改查接口開發(fā)+測試(超詳細(xì)建議收藏)的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!