郑州模板网站制作,免费网站建设制作哪家公司好,各大网站博客怎么做推广,网站开发做网站前言
Java也是测试必知必会的内容#xff0c;特别是现在类似spring boot 等Java框架更是成为主流。之前实现的图书增删改查是用Python实现的#xff0c;没看过的请移步#xff1a;Flaskmysql 实现增删改查接口开发测试#xff08;图文教程附源码#xff09;#xff0c;本…前言
Java也是测试必知必会的内容特别是现在类似spring boot 等Java框架更是成为主流。之前实现的图书增删改查是用Python实现的没看过的请移步Flaskmysql 实现增删改查接口开发测试图文教程附源码本次给大家带来用Java实现的后端接口版本并根据之前的项目总结有做一些优化
一、接口回顾
Python版本增删改查成功示例
1.1 图书增加
请求示例
{title:《Flask框架》,author:pycharm,read_status:1
}
响应示例
{message: 图书添加成功!,status: success
}
完整截图 1.2 图书删除
请求示例
{id:49
}
响应示例
{message: 图书被删除!,status: success
}
完整截图 1.3 图书修改
请求示例
{id: 70,title: 《默8888888888》,author: 陈玉名,read_status: 1
}
响应示例
{message: 图书已更新!,status: success
}
完整截图 1.4 图书查询
请求示例
{id:55
}
响应示例
{data: [{author: 罗贯中,id: 55,read_status: true,title: 《三国演义》}],message: 图书查询成功!,status: success
}
完整截图 1.5 分页查询
请求示例
{page: 1,pagesize: 5
}
响应示例
{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
}
完整截图 上面我们对相关接口的请求入参响应复习了一遍注意请求结构、字段类型、请求方法、请求地址、接口响应格式、接口响应字段类型
下面我们就要用spring boot分层开发实现以上几个接口把查询和分页查询合并一个接口这也是一个优化点
二、技术准备
之前博客有简单使用springboot、Mybatis等的文章本篇文章再次讲述下相关知识
2.1 spring boot
Spring Boot 是一个用于快速开发应用程序的框架它建立在 Spring 框架的基础之上旨在简化配置和部署过程使开发者能够更专注于业务逻辑的实现。
以下是 Spring Boot 的一些主要特点 简化配置 Spring Boot 提供了默认的配置选项减少了开发者在配置方面的工作量。它使用约定大于配置的原则根据项目的依赖和类型自动配置应用程序的不同部分。 内嵌式 Web 服务器 Spring Boot 集成了多种内嵌式 Web 服务器如Tomcat、Jetty和Undertow使得构建和运行 Web 应用变得更加简单。 自动化依赖管理 Spring Boot 可以管理项目的依赖自动选择合适的版本简化了依赖管理的复杂性。 自动化配置 Spring Boot 根据项目的依赖和需要自动进行一些常见配置如数据源、消息队列等减少了手动配置的步骤。 开箱即用的功能 Spring Boot 提供了许多常见功能的开箱即用实现如安全认证、缓存、监控等减少了开发者编写大量样板代码的工作。 简化部署 Spring Boot 支持将应用程序打包为可执行的 JAR 或 WAR 文件以及支持 Docker 容器化部署使部署变得更加方便。 微服务支持 Spring Boot 能够与 Spring Cloud 结合使用为微服务架构提供支持帮助开发者构建分布式系统。
总之Spring Boot 旨在简化 Spring 应用程序的开发、测试、部署和运维过程使开发者能够更快速、更高效地开发出高质量的应用程序。它适用于各种规模的项目从小型应用到大型企业级系统。
2.2 Mybatis
MyBatis之前称为iBATIS是一个开源的持久层框架用于将 Java 对象映射到关系型数据库中的数据。它提供了一种将 SQL 查询、更新和删除等操作与 Java 对象之间的映射关系的方式从而使数据库操作更加简化和灵活。
以下是 MyBatis 的一些主要特点 灵活的SQL映射 MyBatis 允许开发者编写原生的 SQL 查询而不需要过多的 ORM对象关系映射层。这使开发者能够更精确地控制 SQL 查询的执行并更好地优化数据库操作。 参数绑定 MyBatis 提供了参数绑定的功能可以将 Java 对象中的属性值绑定到 SQL 查询中的参数位置从而更方便地执行动态 SQL 查询。 结果映射 MyBatis 可以将查询结果映射为 Java 对象开发者可以通过配置来指定查询结果与 Java 对象之间的映射关系。 支持存储过程和高级映射 MyBatis 支持存储过程调用和一些复杂的结果映射使开发者能够处理更复杂的数据库操作。 缓存支持 MyBatis 提供了一些级别的缓存支持可以提高查询的性能。 插件机制 MyBatis 允许开发者编写插件来扩展框架的功能例如添加自定义的拦截器、执行器等。 XML配置和注解 MyBatis 支持通过 XML 配置文件或者注解的方式来配置映射关系和查询。 与各种数据库兼容 MyBatis 支持多种数据库并且不需要太多的配置就可以适应不同数据库的特性。
总之MyBatis是一个轻量级的持久层框架它通过提供简洁的方式来操作数据库使开发者能够更加灵活和高效地进行数据库操作而无需深入学习复杂的 ORM 框架。
2.3 Maven
Maven 是一个流行的项目构建和管理工具用于帮助开发团队管理项目的构建、依赖管理和文档生成等任务。它提供了一种标准化的方式来管理项目的生命周期从而简化了项目构建、发布和维护过程。
以下是 Maven 的一些主要特点和功能 项目结构标准化 Maven 鼓励使用一种特定的项目结构包括源代码目录、资源文件目录、测试目录等。这有助于团队成员更容易理解和协同开发项目。 依赖管理 Maven 使用一个称为 坐标 的标识符来唯一标识项目的依赖库。通过在项目的配置文件中声明依赖Maven 可以自动下载和管理所需的库文件。 自动构建 Maven 使用 POM项目对象模型文件来描述项目的构建配置包括源代码目录、依赖、插件等。开发者只需编写 POM 文件Maven 就能自动执行构建任务。 插件体系 Maven 提供了丰富的插件来执行各种任务例如编译、测试、打包、部署等。开发者可以通过配置插件来定制项目的构建过程。 生命周期和阶段 Maven 将项目的构建过程划分为生命周期Lifecycle每个生命周期又分为不同的阶段Phase。例如项目的生命周期包括 validate、compile、test、package、install、deploy 等阶段。 仓库管理 Maven 使用中央仓库作为默认的依赖库开发者可以将自己的构建产物发布到本地仓库或者远程仓库。 多模块支持 Maven 支持将一个大型项目划分为多个子模块每个模块可以有自己的 POM 文件和构建配置从而更好地管理复杂的项目结构。 文档生成 Maven 可以通过插件自动生成项目的文档包括代码文档、用户文档等。
总之Maven 是一个用于项目构建和管理的工具它提供了一套规范和标准使开发者能够更高效地进行项目的构建、依赖管理和发布等任务。
2.4 MySQL
MySQL 是一种开源的关系型数据库管理系统RDBMS它是一种广泛使用的数据库技术用于存储和管理结构化数据。MySQL 最初由瑞典公司 MySQL AB 开发后来被 Sun Microsystems 收购再后来 Sun Microsystems 被 Oracle Corporation 收购因此 MySQL 也被称为 Oracle MySQL。
以下是 MySQL 数据库的一些主要特点和功能 关系型数据库 MySQL 是一种关系型数据库数据以表格的形式存储表格中的数据之间通过键值关系相互连接。 开源和免费 MySQL 是开源软件因此可以免费使用和分发。开源性质使得它受到广泛的社区支持和贡献。 跨平台支持 MySQL 可以在多种操作系统上运行包括 Windows、Linux、macOS 等。 性能优化 MySQL 在读写速度上具有优异的性能特别是对于高并发读取请求和大规模数据操作。 数据安全性 MySQL 提供了数据的访问控制、权限管理以及数据加密等功能以确保数据的安全性和保密性。 事务支持 MySQL 支持事务可以保证在一系列操作中的原子性、一致性、隔离性和持久性ACID 属性。 存储引擎 MySQL 支持多种存储引擎如 InnoDB、MyISAM 等不同的存储引擎提供了不同的性能和特性。 SQL 支持 MySQL 使用结构化查询语言SQL作为与数据库进行交互的标准语言。 复制和集群 MySQL 提供了复制和集群功能允许在多台服务器之间同步数据实现高可用性和负载均衡。 备份和恢复 MySQL 支持数据备份和恢复使用户能够定期保存数据以应对意外数据丢失。
总之MySQL 是一款功能强大的开源关系型数据库管理系统适用于各种规模的应用从个人项目到大型企业应用都可以使用它来存储和管理数据。
三、工具准备
上面是理论这一篇我们动手前检查一下环境
3.1 IDEA
首先装IDEA前确保你本地有安装java环境本篇才用jdk 17安装教程有很多此处不再介绍
我用的是2020的pojie版本 3.2 Navicat
确保电脑安装了mysql或者其他机器上。Navicat安装也简单此处也不再赘述 主要是为了验证数据方便
这里为了和Python版本区分我另起了一个book表之前的是books并把read_status设计为int型。在实际工作中也是这样有多个枚举值的定义为int后期好拓展。
重新设计的表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 阅读状态0未读1已读,PRIMARY KEY (id)
) ENGINEInnoDB AUTO_INCREMENT73 DEFAULT CHARSETutf8;
四、项目工程结构
4.1 项目目录结构 4.2 项目目录文件解析
4.2.1 config
包下有2个类
DataSourceConfig.java、MyBatisConfig.java这2个是Mybatis调试过程中禁用了Spring Boot默认的数据源自动配置 学习的文件暂时不用管。
4.2.2 controller
包下有1个类BookController 用来处理前端请求的前端或者postman请求首先会到达这一层也叫控制层
4.2.3 domain
包 下有3个类
BaseInput封装请求用的实际项目会用上此处没时间封装请求
Book最主要的实体类或者模型类字段和setter、getter方法都在这里注意实际项目会采用Data注解等去掉setter、getter方法
ResponseData 封装响应类实际项目也会有
4.2.4 mapper
包下有1个类BookMapper
持久层主要与数据库进行交互。也叫DAO层现在用 Mybatis 逆向工程生成的 mapper 层其实就是 DAO 层。DAO 层会调用entity(domain)层DAO 中会定义实际使用到的方法比如增删改查
4.2.5 service
包下有1个类 BookService 1个包impl包下有服务实现类 BookServiceImpl、自定义异常类BookNotFoundException
4.2.6 启动类
和上面 包平级的BookApplication类是spring boot项目默认生成的启动类
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存放一些依赖可以在这里引入依赖更改依赖
一定要注意目录结构否则会有各种报错。
关于spring boot各层的详细解析参考我的博文Spring Boot 各层作用与联系 五、增删改查接口开发
前端请求过来遵循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;Autowiredpublic BookController(BookService bookService){this.bookService bookService;}PostMapping(/add)public ResponseEntityMapString, String addBook(RequestBody Book book) {try {//System.out.println(前端传值ReadStatusbook.getReadStatus());bookService.insertData(book);MapString, String response new HashMap();response.put(message, 图书添加成功!);response.put(status, success);return ResponseEntity.ok(response);} catch (IllegalArgumentException e) {MapString, String response new HashMap();response.put(message, e.getMessage());response.put(status, fail);return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(response);}}代码点评
这段代码是一个 Spring Boot 控制器Controller用于处理关于图书Book的 HTTP 请求。下面对其中的主要部分进行解释 RequestMapping(/api/book): 这个注解将类映射到 /api/book 路径表示这个控制器处理与图书相关的 API 请求。 private final BookService bookService;: 这是一个私有的成员变量类型为 BookService用于在控制器中调用与图书相关的业务逻辑方法。 Autowired: 这个注解用于自动装配 BookService 实例即将 bookService 成员变量与 Spring 容器中的 BookService 对象关联起来。 PostMapping(/add): 这个注解表示该方法处理 HTTP POST 请求路径为 /add用于添加图书信息。 public ResponseEntityMapString, String addBook(RequestBody Book book): 这是处理添加图书请求的方法。它接收一个 HTTP 请求体中的 JSON 数据并将其映射为一个 Book 对象。返回类型为 ResponseEntityMapString, String即返回一个带有消息和状态信息的响应。 try: 这是一个异常处理的开始表示尝试执行以下代码块如果出现异常则跳到 catch 部分进行处理。 bookService.insertData(book);: 调用 bookService 的 insertData 方法来将传入的图书信息插入到数据库中。 MapString, String response new HashMap();: 创建一个 HashMap 作为响应体用于存储返回给客户端的消息和状态信息。 response.put(message, 图书添加成功!);: 将消息和状态信息放入响应体中。 response.put(status, success);: 将操作状态放入响应体中。 return ResponseEntity.ok(response);: 返回一个 HTTP 响应状态码为 200成功将上述响应体包装在 ResponseEntity 中。 catch (IllegalArgumentException e) { ... }: 这是异常处理的部分如果在 try 块中出现了 IllegalArgumentException 异常就会执行这里的代码。 在 catch 部分中创建一个包含错误消息和状态的响应体然后使用 ResponseEntity.status(HttpStatus.BAD_REQUEST).body(response) 返回一个 HTTP 响应状态码为 400客户端请求错误将错误信息包装在 ResponseEntity 中。
总之这段代码定义了一个用于处理添加图书请求的 Spring Boot 控制器方法。它接收来自客户端的图书数据将数据传递给业务逻辑层进行处理然后返回适当的响应消息和状态信息。
5.1.2 BookService.java
void insertData(Book book); 代码点评 void insertData(Book book); 是一个方法声明它位于某个 Java 接口或抽象类中用于定义一个接口方法。这个方法声明的含义如下 void: 方法返回类型。这里是 void表示这个方法没有返回值即不返回任何数据。 insertData: 方法名称。这个方法被称为 insertData这是一个自定义的方法名用于表示将一本图书信息插入到某个数据源如数据库中。 (Book book): 方法参数。这里的 (Book book) 表示这个方法接受一个名为 book 的参数参数类型是 Book 类。这个参数将用于传递要插入的图书信息。
综合起来这个方法声明表示一个用于将图书信息插入到数据源中的接口方法。实际的实现代码应该在某个类中实现了包含这个方法声明的接口以便在调用时执行插入操作。在你的代码示例中这个方法是在 BookService 接口中声明的具体的实现应该在 BookServiceImpl 类中
5.1.3 BookServiceImpl.java Overridepublic void insertData(Book book) {if (book.getTitle() null || book.getAuthor() null || book.getReadStatus() null) {throw new IllegalArgumentException(title、author和read_status是必传参数);}// Check for duplicate titleString title book.getTitle().trim();if (bookMapper.existsByTitle(title)) {throw new IllegalArgumentException(书名title重复);}bookMapper.insertData(book);
}
代码点评
这段代码是一个方法的实现它覆盖Override了一个接口或父类中的同名方法。具体解释如下 Override: 这是一个注解表示这个方法是对父类或接口中同名方法的重写。在 Java 中如果一个类实现了某个接口或者一个类继承了另一个类可以通过使用 Override 注解来表明某个方法是对父类或接口中方法的重写。 public void insertData(Book book) { ... }: 这是一个方法的实现。它覆盖了某个接口或父类中声明的 insertData 方法。方法签名中的参数类型和返回类型必须与被重写的方法一致。 方法体: 方法体包含了实际的代码逻辑。这段代码的目的是在插入图书信息之前进行一些验证和处理 首先检查传入的 book 对象的 title、author 和 readStatus 是否为 null。如果有任何一个为 null就会抛出 IllegalArgumentException 异常提示必要的参数不能为空。 然后对传入的 title 进行修整去掉前后的空格以确保数据的准确性。 接着使用 bookMapper.existsByTitle(title) 方法检查数据库中是否已经存在具有相同标题的图书。如果存在重复的标题会抛出 IllegalArgumentException 异常提示标题重复。 最后如果验证通过调用 bookMapper.insertData(book) 方法将传入的图书信息插入到数据源中。
总之这段代码实现了向数据源插入图书信息的操作并在插入前进行了一些数据验证和处理以确保数据的完整性和准确性。
5.1.4 BookMapper.java void insertData(Book book);
代码点评
在 MyBatis 中Mapper 接口的方法声明定义了对数据库的操作。这个 void insertData(Book book); 就是一个 Mapper 接口中的方法声明它指定了一个用于向数据库插入图书信息的操作。
具体解释如下 void insertData(Book book);: 这是一个接口中的方法声明。它告诉 MyBatis 框架在映射配置文件中找到名为 insertData 的 SQL 语句并将参数 book 中的数据插入到数据库中。 void: 表示该方法没有返回值。insertData: 方法的名称与映射配置文件中的 insert 元素的 id 属性对应。(Book book): 方法的参数表示插入操作需要传入一个名为 book 的参数它的类型是 Book 类或其子类。
这样当你在 MyBatis 映射配置文件中配置了一个名为 insertData 的 insert 元素定义了插入操作的 SQL 语句并在代码中调用了 insertData(book) 方法时MyBatis 会根据配置文件的定义执行对应的 SQL 语句将 book 对象的数据插入到数据库中。
5.1.5 BookMapper.xml !-- 增加 --insert idinsertDataINSERT INTO book (title, author, read_status)VALUES (#{title}, #{author}, #{readStatus})/insert
代码点评
这段 XML 代码是 MyBatis 映射文件中的一个 SQL 映射它定义了一个插入操作用于向数据库中的 book 表中插入数据。具体解释如下 insert idinsertData: 这是一个插入操作的声明通过 id 属性指定了该操作的标识名这样在 Java 代码中就可以通过这个标识名来调用这个操作。 INSERT INTO book (title, author, read_status): 这是 SQL 语句的一部分表示将数据插入到 book 表中指定了插入的目标表和需要插入的字段。 VALUES (#{title}, #{author}, #{readStatus}): 这也是 SQL 语句的一部分用来指定插入的具体数值。#{title}, #{author}, #{readStatus} 是 MyBatis 的占位符语法表示这里会将 Java 对象中的属性值填充进去。 #{title}: 这是占位符会被实际的 title 属性值替代。#{author}: 同样是占位符会被实际的 author 属性值替代。#{readStatus}: 这也是占位符会被实际的 readStatus 属性值替代。
综合起来这段 XML 代码定义了一个插入操作会将传入的 title、author 和 readStatus 属性值插入到数据库的 book 表中的相应字段中。
5.2 图书删除接口
5.2.1 controller.java public ResponseEntityMapString, String deleteById(RequestBody MapString, Object requestParams) {MapString, Object data (MapString, Object) requestParams.get(data);Long id Long.valueOf(data.get(id).toString());// 构建返回结果boolean isDeleted bookService.deleteById(id);if (isDeleted) {MapString, String successResponse new HashMap();successResponse.put(message, 图书被删除!);successResponse.put(status, success);return ResponseEntity.ok(successResponse);} else {MapString, String failResponse new HashMap();failResponse.put(message, 需要删除的图书不存在!);failResponse.put(status, fail);return ResponseEntity.status(HttpStatus.NOT_FOUND).body(failResponse);}}代码点评
这段代码是一个 Spring Boot 控制器方法用于处理删除图书的请求。下面是代码的解释 public ResponseEntityMapString, String deleteById(RequestBody MapString, Object requestParams) {: 这是一个控制器方法的声明。它使用了 RequestBody 注解表示请求体中的数据会被映射为一个 Map 对象并且返回一个 ResponseEntity 对象该对象包含了响应数据以及 HTTP 状态码等信息。 MapString, Object data (MapString, Object) requestParams.get(data);: 这行代码从请求参数的 data 字段中获取了一个嵌套的 Map 对象这个嵌套的 Map 包含了需要的删除操作数据。 Long id Long.valueOf(data.get(id).toString());: 这行代码从嵌套的 Map 中获取了键为 id 的值并将其转换为 Long 类型这个 id 表示要删除的图书的唯一标识。 boolean isDeleted bookService.deleteById(id);: 这行代码调用了一个 bookService 的方法来进行实际的删除操作它会尝试根据传入的 id 来删除对应的图书数据并返回一个布尔值表示是否删除成功。 if (isDeleted) { ... } else { ... }: 这是一个条件语句用于根据删除操作的结果返回不同的响应。如果删除成功就返回一个成功的响应其中包含成功的消息和状态。如果删除失败就返回一个失败的响应其中包含失败的消息和状态并且设置了 HTTP 响应状态码为 404NOT FOUND。
综合起来这段代码实现了一个删除图书的功能它会从请求体中获取图书的唯一标识 id然后调用服务层的方法来删除图书并根据删除操作的结果返回不同的响应。
5.2.2 BookService.java boolean deleteById(Long id);boolean existsByTitle(Param(title) String title);
代码点评
当你需要删除一本图书时你可以调用 boolean deleteById(Long id); 方法。这个方法通常是在一个服务接口或类中定义的。它使用图书的唯一标识符id来从数据库中删除对应的图书。方法的工作方式如下 Long id这个参数表示要删除的图书的唯一标识符。 boolean方法的返回类型表明删除是否成功。如果图书成功删除方法返回 true如果删除失败返回 false。
另一个方法是 boolean existsByTitle(Param(title) String title);。这个方法也通常在一个服务接口或类中定义。它用于检查数据库中是否存在具有给定标题的图书。方法的工作方式如下 Param(title) String title这个参数表示要检查的图书的标题。 boolean方法的返回类型表明具有给定标题的图书是否存在于数据库中。如果存在方法返回 true如果不存在返回 false。
这两个方法经常结合使用以执行与在数据库中管理图书相关的各种操作。deleteById 方法负责根据图书的 ID 删除图书而 existsByTitle 方法有助于确定数据库中是否已经存在具有特定标题的图书。
5.2.3 BookServiceImpl.java Overridepublic boolean deleteById(Long id) {if (id null || id 0) {throw new IllegalArgumentException(无效的图书 ID);}int rowsAffected bookMapper.deleteById(id);return rowsAffected 0;}
代码点评
这段代码是一个方法实现用于从数据库中删除一本图书。下面是对这段代码的解释 Override这个注解表示这个方法是重写自父类或接口的方法。 public boolean deleteById(Long id)这是一个公共方法它的返回类型是布尔值 boolean。它接受一个类型为 Long 的参数 id表示要删除的图书的唯一标识符。 if (id null || id 0)这是一个条件语句用于检查传入的图书标识符是否为无效值。如果 id 为 null 或小于等于零就会抛出一个 IllegalArgumentException 异常提示图书 ID 无效。 int rowsAffected bookMapper.deleteById(id);这一行调用了名为 bookMapper 的对象的 deleteById 方法传入要删除的图书的 ID。这个方法将在数据库中执行删除操作并返回受影响的行数即删除的行数。 return rowsAffected 0;根据执行删除操作后受影响的行数这个表达式返回一个布尔值。如果受影响的行数大于零表示删除成功返回 true否则返回 false。这是为了指示删除操作是否成功完成。
这个方法的作用是接收一个图书的唯一标识符然后调用底层的数据库访问方法将对应的图书从数据库中删除。如果删除成功受影响的行数大于零方法返回 true否则返回 false。
5.2.4 BookMapper.java
int deleteById(Param(id) Long id);
代码点评
这段代码是一个 MyBatis Mapper 接口中的方法定义用于在数据库中根据图书的 ID 删除记录。下面是对这段代码的解释 int deleteById(Param(id) Long id);这是一个接口方法定义返回类型是整型 int。它使用了 MyBatis 的 Param 注解指定了参数名称和对应的映射关系。方法接受一个类型为 Long 的参数 id表示要删除的图书的唯一标识符。 Param(id)这是 MyBatis 的注解它用于为方法参数指定一个映射名称这里是 id。在 SQL 语句中可以通过 #{id} 来引用这个参数。 int方法的返回类型是整型 int表示删除操作受影响的行数。
这个方法的作用是为 MyBatis 映射文件提供一个接口通过调用这个接口的方法可以在数据库中执行根据图书 ID 进行删除操作。在映射文件中可以通过使用 #{id} 来获取传递的图书 ID 参数并执行相应的删除 SQL 操作。方法的返回值表示删除操作后受影响的行数可以用于判断删除是否成功。
5.2.5 BookMapper.xml !-- 判断是否存在指定 title 的记录 --select idexistsByTitle resultTypeboolean parameterTypejava.lang.StringSELECT COUNT(*) 0FROM bookWHERE title #{title}/select!-- 删除 --delete iddeleteById parameterTypelongDELETE FROM book WHERE id #{id}/delete
代码点评
这段代码是 MyBatis 映射文件中的两个 SQL 查询语句分别用于判断指定标题的记录是否存在以及删除指定 ID 的记录。下面是对这两段代码的解释 判断是否存在指定 title 的记录 select idexistsByTitle resultTypeboolean parameterTypejava.lang.String这是一个查询语句的定义通过 id 属性指定查询的标识符resultType 属性指定查询结果的返回类型这里是 boolean。parameterTypejava.lang.String这个属性指定了输入参数的数据类型这里是一个字符串类型的标题。SELECT COUNT(*) 0 FROM book WHERE title #{title}这是查询的 SQL 语句。它使用了 COUNT(*) 来计算满足条件的记录数是否大于 0如果大于 0则说明存在指定标题的记录。其中 #{title} 是一个占位符表示参数传递的标题。 删除 delete iddeleteById parameterTypelong这是一个删除语句的定义通过 id 属性指定删除的标识符parameterType 属性指定了传递给 SQL 语句的参数类型这里是 long 类型的 ID。DELETE FROM book WHERE id #{id}这是删除的 SQL 语句。它会根据传递的 id 参数删除数据库中对应的记录。其中 #{id} 是一个占位符表示参数传递的图书 ID。
这两个 SQL 查询语句分别用于在数据库中判断某个标题的记录是否存在返回一个布尔值以及根据图书 ID 删除记录。这些语句会与 MyBatis 中的方法关联通过方法调用和参数传递来执行相应的数据库操作。
5.3 图书修改接口
5.3.1 controller.java PostMapping(/update)public ResponseEntityResponseData 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);}}代码点评
这段代码是一个 Spring Boot 控制器中的方法用于处理图书更新的请求。下面是对这段代码的解释 PostMapping(/update)这是一个注解表示这个方法会处理 POST 请求并且路径是 /update。当客户端发起一个 POST 请求到 /update 路径时该方法将被调用。 public ResponseEntityResponseData updateBook(RequestBody Book book)这是方法的签名它指定了方法的访问修饰符、返回值类型以及方法名。RequestBody 注解表示从请求体中获取参数这里是一个 Book 对象即请求中传递的要更新的图书信息。方法返回一个 ResponseEntityResponseData表示响应实体其中包含了响应的数据和状态信息。 try这是一个异常处理的起始块。在这里我们尝试执行可能会抛出异常的代码块。 bookService.updateData(book);这是调用业务逻辑层的 updateData 方法传递了要更新的图书信息。这个方法会处理图书的更新操作。 ResponseData response new ResponseData(success, 图书已更新!);在更新操作成功后创建一个名为 response 的 ResponseData 对象其中包含了成功状态和成功的消息。 return ResponseEntity.ok(response);将上面创建的 response 对象作为响应体使用 ResponseEntity.ok() 方法构建一个成功响应实体将它返回给客户端。 catch (IllegalArgumentException e)这是一个异常捕获块用于捕获可能抛出的 IllegalArgumentException 异常。 ResponseData response new ResponseData(fail, e.getMessage());在出现参数错误的情况下创建一个名为 response 的 ResponseData 对象其中包含了失败状态和异常消息。 return ResponseEntity.badRequest().body(response);将上面创建的 response 对象作为响应体使用 ResponseEntity.badRequest() 方法构建一个错误的响应实体将它返回给客户端表示请求参数错误。 catch (BookNotFoundException e)这是另一个异常捕获块用于捕获可能抛出的 BookNotFoundException 异常。 ResponseData response new ResponseData(fail, 需要修改的书籍id不存在!);在找不到需要修改的书籍 ID 的情况下创建一个名为 response 的 ResponseData 对象其中包含了失败状态和消息。 return ResponseEntity.status(HttpStatus.NOT_FOUND).body(response);将上面创建的 response 对象作为响应体使用 ResponseEntity.status(HttpStatus.NOT_FOUND) 方法构建一个带有 HTTP 状态码 404 的响应实体将它返回给客户端表示找不到要修改的书籍。
这段代码实现了在更新图书信息时的异常处理和响应生成根据业务逻辑和参数情况构建不同的响应实体并返回给客户端。
5.3.2 BookService.java void updateData(Book book);
代码点评
这段代码是一个服务类中的方法定义用于更新图书信息。下面是对这段代码的解释 void这是方法的返回类型表示该方法不返回任何值。 updateData这是方法的名称用于表示这个方法是用来执行更新操作的。 (Book book)这是方法的参数列表其中 Book 是一个数据类型表示要传递的参数是一个名为 book 的 Book 对象。这个对象包含了需要更新的图书的信息。
所以这段代码定义了一个名为 updateData 的方法它接受一个 Book 对象作为参数该方法的作用是将传递进来的 Book 对象中的图书信息用于更新操作。具体的更新操作逻辑将在方法的实现中进行处理。
5.3.3 BookServiceImpl.java Overridepublic 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是必传参数);}ListBook bookList bookMapper.selectAll();ListInteger 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(阅读状态只能为0和1!);}bookMapper.updateData(book.getId().longValue(), title, author, readStatus);}代码点评
这段代码是一个服务实现类中的方法用于更新图书信息。下面是对这段代码的解释
该方法首先检查传递进来的 Book 对象中是否有必要的字段信息id、title、author 和 readStatus。然后它获取数据库中的已存在图书列表检查要修改的图书的 id 是否存在于列表中。接着它处理图书的标题、作者和阅读状态的信息进行必要的检查。最后它调用数据访问层的方法 updateData 进行实际的更新操作将新的图书信息应用到数据库中。如果任何检查不通过将抛出异常。
5.3.4 BookMapper.java void updateData(Param(id) Long id, Param(title) String title, Param(author) String author, Param(readStatus) Integer readStatus);代码点评
这段代码是一个MyBatis Mapper接口中的方法定义用于更新图书信息。这里使用了Param注解来指定方法参数与SQL语句中的参数对应关系。下面是对这段代码的解释
这个方法定义了四个参数分别是id、title、author和readStatus。这些参数用于更新图书的相关信息。 Param(id) Long id: 这个参数用于指定要更新的图书的唯一标识符ID。在SQL语句中会使用#{id}来引用这个参数。 Param(title) String title: 这个参数用于指定要更新的图书的标题。在SQL语句中会使用#{title}来引用这个参数。 Param(author) String author: 这个参数用于指定要更新的图书的作者。在SQL语句中会使用#{author}来引用这个参数。 Param(readStatus) Integer readStatus: 这个参数用于指定要更新的图书的阅读状态。在SQL语句中会使用#{readStatus}来引用这个参数。
该方法在调用时会将这些参数传递给MyBatis框架框架会根据参数的名称将它们映射到对应的SQL语句中。具体的SQL语句是在XML映射文件中定义的它会使用这些参数来执行更新操作将新的图书信息应用到数据库中
5.3.5 BookMapper.xml !-- 修改 --update idupdateDataUPDATE book SETtitle #{title},author #{author},read_status #{readStatus}WHERE id #{id}/update
代码点评
这段XML代码是一个MyBatis的映射文件中的一个SQL语句定义用于更新图书信息。下面是对这段代码的解释
这个update元素定义了一个更新操作的SQL语句。它使用了MyBatis的占位符语法#{}来引用参数具体含义如下 UPDATE book SET: 这是SQL语句的更新部分表示要对book表进行更新操作。 title #{title},: 这里使用了占位符语法#{title}表示要将title参数的值赋给数据库中的title列。 author #{author},: 类似地将author参数的值赋给数据库中的author列。 read_status #{readStatus}: 同样将readStatus参数的值赋给数据库中的read_status列。 WHERE id #{id}: 这个部分指定了更新的条件只会对id等于#{id}的记录进行更新。
在使用这个SQL语句时MyBatis会将实际的参数值替换到占位符#{}中然后执行这个更新操作。这样就能根据传递的参数更新图书的标题、作者和阅读状态
5.4 图书查询接口
5.4.1 controller.java PostMapping(/query)public ResponseDataListBook queryBookByTitle(RequestBody MapString, Object requestParams) {MapString, Object data (MapString, 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;ListBook result bookService.queryBookByTitle(title, pagesize, page);if (result.isEmpty()) {return new ResponseData(需要查询的图书不存在!, fail, result);}return new ResponseData(请求成功, success, result);}
代码点评
这段代码是一个控制器Controller中的一个方法用于处理通过标题查询图书的请求。以下是代码的解释 PostMapping(/query): 这是一个用于处理 HTTP POST 请求的注解指定了请求的路径为 /query。 public ResponseDataListBook queryBookByTitle(RequestBody MapString, Object requestParams): 这是一个方法定义它接受一个 Map 对象作为请求体并返回一个 ResponseData 类型的对象表示响应数据。 MapString, Object data (MapString, Object) requestParams.get(data);: 从传入的请求体中获取名为 data 的字段它应该是一个包含请求参数的 Map 对象。 String title (String) data.get(title);: 从获取到的数据中获取 title 字段这是用于查询的图书标题。 int pagesize ...: 获取 pagesize 和 page 字段这些字段指定了分页的大小和页码默认为 3 和 1。 ListBook result bookService.queryBookByTitle(title, pagesize, page);: 使用获取到的标题、分页大小和页码调用业务逻辑层的方法来查询图书数据。 if (result.isEmpty()) { ... }: 检查查询结果是否为空。如果为空创建一个新的 ResponseData 对象其中包含 需要查询的图书不存在! 的消息和 fail 的状态。 return new ResponseData(请求成功, success, result);: 如果查询结果不为空创建一个新的 ResponseData 对象其中包含 请求成功 的消息和 success 的状态同时将查询结果作为数据。
综合起来这个方法通过标题查询图书如果查询结果为空则返回一个包含错误消息和 fail 状态的响应对象如果查询结果不为空则返回一个包含成功消息和 success 状态以及查询结果数据的响应对象
5.4.2 BookService.java ListBook queryBookByTitle(String title, Integer pagesize, Integer page);代码点评
这段代码是服务层Service Layer接口中的一个方法声明用于根据书名、每页条数和页码来查询图书信息。下面是对这段代码的解释
这个方法声明了一个名为 queryBookByTitle 的方法它接受三个参数 title表示要查询的书名。这是一个字符串类型的参数用于指定要查询的书名关键字。 pagesize表示每页显示的条数。这是一个整数类型的参数用于指定每页要显示的图书条目数。 page表示要查询的页码。这是一个整数类型的参数用于指定要查询的页码编号。
方法声明的返回类型是一个 ListBook这表示该方法将会返回一个由 Book 对象组成的列表这些 Book 对象是根据查询条件获取到的图书信息。
这个方法的作用是在服务层中根据给定的书名、每页条数和页码来执行图书查询操作然后返回查询结果。通常该方法会调用底层的数据库查询方法获取满足条件的图书数据并将结果转化为 Book 对象列表返回给调用方
5.4.3 BookServiceImpl.java Overridepublic ListBook queryBookByTitle(String title, Integer pagesize, Integer page) {int offset pagesize * (page - 1);RowBounds rowBounds new RowBounds(offset, pagesize);return bookMapper.queryBookByTitle(title, rowBounds);}
代码点评
这段代码是服务层Service Layer中的方法实现用于根据书名、每页条数和页码来查询图书信息。下面是对这段代码的解释
这个方法实现了先前声明的 queryBookByTitle 方法。它接受三个参数
title要查询的书名关键字。pagesize每页显示的图书条目数。page要查询的页码。
在方法中首先计算出数据的偏移量offset这个偏移量是通过页码和每页条数计算得到的用于确定从数据库中哪条记录开始获取数据。
然后创建了一个 RowBounds 对象这个对象用于在数据库查询中指定偏移量和限制的条目数实现分页查询。
最后调用了 bookMapper.queryBookByTitle 方法将书名关键字和分页信息传递给数据库查询方法。查询结果会根据传入的分页信息获取相应的图书数据并将查询结果以 ListBook 的形式返回给调用方。
总之这个方法的作用是在服务层中使用 MyBatis 的分页功能根据给定的书名关键字、每页条数和页码执行图书查询操作并返回查询结果
5.4.4 BookMapper.java ListBook queryBookByTitle(Param(title) String title, RowBounds rowBounds);ListBook selectAll();
代码点评
这两个方法是 MyBatis 的 Mapper 接口中定义的查询方法用于在数据库中进行数据查询操作。下面分别解释这两个方法 ListBook queryBookByTitle(Param(title) String title, RowBounds rowBounds); 这个方法用于根据书名关键字和分页信息来查询图书列表。下面是对方法的各部分进行解释 Param(title) String title这个注解是用于为方法参数指定名称的 MyBatis 注解。在查询语句中可以通过该名称引用这个参数。在这个方法中它用于指定查询图书时要匹配的书名关键字。RowBounds rowBounds这是 MyBatis 提供的一个分页信息对象它包含偏移量和限制条数。通过传递这个对象可以在查询中实现分页功能。 ListBook selectAll(); 这个方法用于查询所有图书的信息返回一个包含所有图书对象的列表。它没有参数因为它是一个简单的查询操作不需要额外的查询条件。
总之这两个方法分别用于根据书名关键字和分页信息查询图书列表以及查询所有图书的信息。它们都是在 MyBatis 的 Mapper 接口中定义的实际的查询操作由 MyBatis 框架负责执行
5.4.5 BookMapper.xml
!-- 查询 --select idqueryBookByTitle resultTypecom.example.book.domain.BookSELECT id,title,author,read_status readStatus FROM bookwhereif testtitle ! null and title ! AND title LIKE CONCAT(%, #{title}, %)/if/where/select!-- 查询所有图书 --select idselectAll resultTypecom.example.book.domain.BookSELECT id, title, author, read_status as readStatusFROM book
代码点评
这两段代码是 MyBatis 的映射文件中的查询语句用于从数据库中获取图书信息。以下是对每个查询语句的解释 1、查询图书根据书名关键字
select 标签定义一个查询语句。idqueryBookByTitle为查询语句指定一个唯一的标识符。resultTypecom.example.book.domain.Book指定查询结果的类型即图书对象的类型。SELECT id, title, author, read_status readStatus FROM book实际的 SQL 查询语句从数据库中选择图书的 id、title、author 和 read_status 字段。where 标签这个标签用于生成 SQL 的 WHERE 子句根据条件动态生成。if testtitle ! null and title ! 条件判断语句判断是否传入了 title 参数并且不为空。AND title LIKE CONCAT(%, #{title}, %)如果满足条件会在 SQL 查询语句中添加类似 AND title LIKE %关键字% 的条件进行模糊匹配书名关键字。 2、查询所有图书 select 标签同样定义一个查询语句。 idselectAll为查询语句指定唯一标识符。resultTypecom.example.book.domain.Book指定查询结果的类型即图书对象的类型。SELECT id, title, author, read_status as readStatus FROM book从数据库中选择图书的 id、title、author 和 read_status 字段。 这两个查询语句定义了如何从数据库中获取图书信息通过 MyBatis 框架进行执行。第一个查询语句允许根据书名关键字进行模糊查询第二个查询语句获取所有图书的信息
六、增删改查接口测试
CRUD增删改查接口已经开发完毕下面我们开始测试。注意这里只列了部分场景。入参也做了校验这里就不列截图了
6.1 图书增加测试
6.1.1 添加图书测试
请求示例
{title:《Spring高级编程》,author:李专家,read_status:1
}
响应示例
{message: 图书添加成功!,status: success
}
完整截图 数据库验证 6.1.2 添加重复图书测试
再次点击报错书名重复 6.1.3 图书添加接口测试结论 新增接口测试通过
6.2 图书删除测试
6.2.1 通过id删除图书测试
准备删除id为 67的书籍 请求示例
{data: {id: 67},extra: {}
}
响应示例
{message: 图书被删除!,status: success
}
完整截图 数据库验证 6.2.2 通过id删除不存在图书测试
再次点击报错图书不存在 6.2.3 图书删除接口测试结论 删除接口测试通过
6.3 图书修改测试
6.3.1 通过修改图书标题测试 修改id 为68的书籍
请求示例
{id: 68,title: 《我是测试开发》,author: 李同学,read_status: 0
}
响应示例
data是拓展字段这里返回null没影响。前端页面暂时不用
{message: success,status: 图书已更新!,data: null
}
完整截图 数据库验证 6.3.2 通过修改图书作者测试
再次修改作业 天下霸唱点击修改返回成功 查看数据库修改成功 6.3.3 图书修改接口测试结论
修改接口测试通过
6.4 图书查询测试
6.4.1 不传书名页数大小10第1页测试
请求示例
{data: {title: ,pagesize: 10,page: 1},extra: {}
}
响应示例
{message: 请求成功,status: success,data: [{id: 68,title: 《我是测试》,author: 李同学,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}]
}
完整截图 数据库验证 6.4.2 关键字过滤查询测试
查询过滤
{data: {title: 编程,pagesize: 10,page: 1},extra: {}
}
结果
{message: 请求成功,status: success,data: [{id: 72,title: 《Java高级编程》,author: 李专家,read_status: 1},{id: 73,title: 《Spring高级编程》,author: 李专家,read_status: 1}]
} 查询成功
6.4.3 所有值不传分页默认传3和1测试
分页查询默认传3和1title不传查所有
{data: {title: ,pagesize: ,page: },extra: {}
}
响应
{message: 请求成功,status: success,data: [{id: 68,title: 《我是测试》,author: 李同学,read_status: 0},{id: 69,title: 《三国演义20》,author: 罗贯中,read_status: 1},{id: 70,title: 《默》,author: 李晓明,read_status: 0}]
} 对照数据库 6.4.4 图书不存在时查询测试
查询不存在的图书时 请求
{data: {title: 粑粑,pagesize: ,page: },extra: {}
}
响应
{message: 需要查询的图书不存在!,status: fail,data: []
} 截图 数据库里没有这个标题相关的书籍 标题不存在图书测试通过 6.4.5 图书查询接口测试结论
查询接口测试通过
七、总结
本次包括接口开发、测试、博文撰写断断续续写了几天总体上比之前flask实现的要好一些。目前这种CRUD基本上可以在项目上使用了。优化点是可以加上更多的校验和更多的封装
八、期望
作为一名测试专家。Java是必须掌握的语言而掌握java必须得有几个框架的项目实战这样才能做好测试。拥有和开发同频对话能力增加测试自信心当然也是能力的体现。后续会根据项目分享更多的白盒测试、单元测试文章共同进步不说了老婆叫我包饺子了。。。
九、源码
本来要放在git上的这一次先全部贴出来。在公司电脑操作涉及安全我还是先保住工作哈哈哈
9.1 DataSourceConfig.java
//这个文件是禁用了Spring Boot默认的数据源自动配置
// 导致报错Consider defining a bean of type javax.sql.DataSource in your configuration
//加了这个文件也能处理这个报错让程序启动
//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?tinyInt1isBitfalseuseUnicodetruecharacterEncodingutf-8autoReconnecttruefailOverReadOnlyfalsezeroDateTimeBehaviorconvertToNulluseSSLfalseserverTimezoneAsia/ShanghaiallowMultiQueriestrue\n)
// .username(XXX)
// .password(XXX)
// .driverClassName(com.mysql.cj.jdbc.Driver)
// .build();
// }
//}9.2 MyBatisConfig.java
//package com.example.book.config;
//这个文件是解决报错Consider defining a bean of type org.apache.ibatis.session.SqlSessionFactory in your configuration.
//这个config包的2个文件是我在启动类那里加了1行SpringBootApplication(exclude DataSourceAutoConfiguration.class)
//禁用了Spring Boot默认的数据源自动配置 导致报错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;Autowiredpublic BookController(BookService bookService){this.bookService bookService;}PostMapping(/add)public ResponseEntityMapString, String addBook(RequestBody Book book) {try {//System.out.println(前端传值ReadStatusbook.getReadStatus());bookService.insertData(book);MapString, String response new HashMap();response.put(message, 图书添加成功!);response.put(status, success);return ResponseEntity.ok(response);} catch (IllegalArgumentException e) {MapString, String response new HashMap();response.put(message, e.getMessage());response.put(status, fail);return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(response);}}PostMapping(/delete)public ResponseEntityMapString, String deleteById(RequestBody MapString, Object requestParams) {MapString, Object data (MapString, Object) requestParams.get(data);Long id Long.valueOf(data.get(id).toString());// 构建返回结果boolean isDeleted bookService.deleteById(id);if (isDeleted) {MapString, String successResponse new HashMap();successResponse.put(message, 图书被删除!);successResponse.put(status, success);return ResponseEntity.ok(successResponse);} else {MapString, 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 ResponseEntityResponseData 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 ResponseDataListBook queryBookByTitle(RequestBody MapString, Object requestParams) {MapString, Object data (MapString, 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;ListBook 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 BaseInputT {
//
// ApiModelProperty(value 实际请求参数数据)
// private T data;
//
// ApiModelProperty(value 扩展数据字典类型key-value结构)
// private MapString,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 文档中描述接口的各个字段属性的用途、数据类型、示例值等信息以便生成更详细的 API 文档。
//Swagger 是一个用于生成和展示 API 文档的工具可以帮助开发人员更好地了解和使用 API。
//具体来说ApiModelProperty 注解可以用于类的属性上用于描述 API 接口的请求或响应对象的字段。这些描述将会被 Swagger 生成的 API 文档所使用。主要用途包括
//描述字段用途 通过 ApiModelProperty 注解你可以为每个字段添加描述解释该字段的用途、作用等使文档更加易读和易理解。
//指定数据类型 你可以通过 dataType 参数来指定字段的数据类型例如字符串、整数、浮点数等。
//提供示例值 使用 example 参数可以为字段提供示例值帮助使用者更好地理解字段的期望值。
//控制是否必填 通过 required 参数你可以指定字段是否为必填项。
//其他属性 还可以设置一些其他属性如是否允许空值、是否隐藏该字段等。
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 阅读状态)JsonProperty(read_status) // 指定 JSON 字段名使得前端传参和read_status对应readStatusprivate Integer readStatus;TableField(exist false)private static final long serialVersionUID 1L;//构造函数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是一个用于封装响应数据的泛型类。
//它用于统一返回给前端的响应格式包含三个属性message、status和data。
//属性说明
//message表示响应的消息或描述信息通常用于描述请求的处理结果或返回的状态信息。
//status表示响应的状态通常用于标识请求处理的状态例如success表示成功error表示失败等。
//data表示响应的数据它是一个泛型参数可以用于存储任意类型的数据比如查询结果、实体对象等。
//例如在查询数据库时可以将查询结果存储在data中返回给前端在处理表单提交时可以将表单提交的数据存储在data中返回。
//构造函数
//ResponseData(String message, String status, T data)该构造函数用于创建ResponseData对象
//并初始化message、status和data属性的值。
//Data是一个Lombok注解它可以自动为类生成一些常用的方法如toString()、equals()、hashCode()、getter和setter方法。使用Data注解可以简化Java类的编写减少样板代码。
//具体来说Data注解为类的所有非静态字段生成以下方法
//toString(): 生成一个默认的toString()方法用于将对象转换为字符串表示。该方法会按照字段的名称和值生成字符串。
//equals(): 生成一个默认的equals()方法用于比较对象是否相等。该方法会比较对象的所有字段是否相等。
//hashCode(): 生成一个默认的hashCode()方法用于计算对象的哈希码。该方法基于对象的所有字段计算哈希码。
//getter和setter: 为所有非静态字段生成getter和setter方法用于获取和设置字段的值。
//使用Data注解的类通常称为数据类它主要用于封装数据而不包含业务逻辑。在编写POJOPlain Old Java Object类时使用Data注解可以简化代码提高代码的可读性和可维护性。
Data
public class ResponseDataT {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);ListBook 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;//创建了BooksServiceImpl类并将其标记为Service以便Spring能够识别并注册它作为bean。
//同时也要确保在BooksServiceImpl的构造函数中使用了Autowired来注入BooksMapper。
//在MyBatis中分页应该使用RowBounds或者使用PageHelper来实现
Service
public class BookServiceImpl implements BookService {private final BookMapper bookMapper;Autowiredpublic BookServiceImpl(BookMapper booksMapper) {this.bookMapper booksMapper;}Overridepublic void insertData(Book book) {if (book.getTitle() null || book.getAuthor() null || book.getReadStatus() null) {throw new IllegalArgumentException(title、author和read_status是必传参数);}// Check for duplicate titleString title book.getTitle().trim();if (bookMapper.existsByTitle(title)) {throw new IllegalArgumentException(书名title重复);}bookMapper.insertData(book);
}Overridepublic 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);
// }Overridepublic 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是必传参数);}ListBook bookList bookMapper.selectAll();ListInteger 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(阅读状态只能为0和1!);}bookMapper.updateData(book.getId().longValue(), title, author, readStatus);}Overridepublic ListBook 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 BaseMapperBook {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);ListBook queryBookByTitle(Param(title) String title, RowBounds rowBounds);ListBook 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 version1.0 encodingUTF-8?
!DOCTYPE mapperPUBLIC -//mybatis.org//DTD Mapper 3.0//ENhttp://mybatis.org/dtd/mybatis-3-mapper.dtdmapper namespacecom.example.book.mapper.BookMapperresultMap idBaseResultMap typecom.example.book.domain.Bookid propertyid columnid jdbcTypeINTEGER/result propertytitle columntitle jdbcTypeVARCHAR/result propertyauthor columnauthor jdbcTypeVARCHAR/result propertyreadStatus columnread_status jdbcTypeINTEGER//resultMapsql idBase_Column_Listid,title,author,read_status/sql!-- 增加 --insert idinsertDataINSERT INTO book (title, author, read_status)VALUES (#{title}, #{author}, #{readStatus})/insert!-- 判断是否存在指定 title 的记录 --select idexistsByTitle resultTypeboolean parameterTypejava.lang.StringSELECT COUNT(*) 0FROM bookWHERE title #{title}/select!-- 删除 --delete iddeleteById parameterTypelongDELETE FROM book WHERE id #{id}/delete!-- 修改 --update idupdateDataUPDATE book SETtitle #{title},author #{author},read_status #{readStatus}WHERE id #{id}/update!-- 查询 --select idqueryBookByTitle resultTypecom.example.book.domain.BookSELECT id,title,author,read_status readStatus FROM bookwhereif testtitle ! null and title ! AND title LIKE CONCAT(%, #{title}, %)/if/where/select!-- 查询所有图书 --select idselectAll resultTypecom.example.book.domain.BookSELECT id, title, author, read_status as readStatusFROM books
/select/mapper9.13 application.properties
注意我加了个1在后缀2个文件选择1个生效即可
spring.datasource.url jdbc:mysql://XX.XX.XXX.24:13300/z_liqiju_test?tinyInt1isBitfalseuseUnicodetruecharacterEncodingutf-8autoReconnecttruefailOverReadOnlyfalsezeroDateTimeBehaviorconvertToNulluseSSLfalseserverTimezoneAsia/ShanghaiallowMultiQueriestrue
spring.datasource.username XXX
spring.datasource.password XXX
mybatis.mapper-locations classpath*:mapper/*.xml
server.port 5002
logging.level.rootinfo
#logging.level.rootDEBUG
#logging.level.org.mybatisDEBUG
9.14 application.yml
原来是的项目是5001 端口这一次改为5002端口启动防止端口冲突
spring:datasource:driver-class-name: com.mysql.jdbc.Driverurl: jdbc:mysql://XX.XX.XXX.24:13300/z_liqiju_test?tinyInt1isBitfalseuseUnicodetruecharacterEncodingutf-8autoReconnecttruefailOverReadOnlyfalsezeroDateTimeBehaviorconvertToNulluseSSLfalseserverTimezoneAsia/ShanghaiallowMultiQueriestrueusername: XXXpassword: XXXmybatis:mapper-locations: classpath*:mapper/*.xmlserver:port: 5002logging:level:root: DEBUG
# org:
# mybatis: INFO
9.15 pom.xml
?xml version1.0 encodingUTF-8?
project xmlnshttp://maven.apache.org/POM/4.0.0 xmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsdmodelVersion4.0.0/modelVersionparentgroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-parent/artifactIdversion3.0.4/versionrelativePath/ !-- lookup parent from repository --/parentgroupIdcom.example/groupIdartifactIdbook/artifactIdversion0.0.1-SNAPSHOT/versionnamebook/namedescriptionDemo project for Spring Boot/descriptionpropertiesjava.version1.8/java.version/propertiesdependencies!-- spring-boot-starter-jdbc 驱动 --dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-jdbc/artifactId/dependency!-- spring-boot-starter-web --dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependencydependencygroupIdorg.mybatis.spring.boot/groupIdartifactIdmybatis-spring-boot-starter/artifactIdversion3.0.0/version/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-devtools/artifactIdscoperuntime/scopeoptionaltrue/optional/dependencydependencygroupIdcom.mysql/groupIdartifactIdmysql-connector-j/artifactIdscoperuntime/scope/dependencydependencygroupIdorg.projectlombok/groupIdartifactIdlombok/artifactIdoptionaltrue/optional/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-test/artifactIdscopetest/scope/dependencydependencygroupIdcom.baomidou/groupIdartifactIdmybatis-plus-annotation/artifactIdversion3.3.1/version/dependencydependencygroupIdcom.baomidou/groupIdartifactIdmybatis-plus-core/artifactIdversion3.3.1/version/dependencydependencygroupIdjunit/groupIdartifactIdjunit/artifactIdversion4.12/versionscopetest/scope/dependencydependencygroupIdcom.baomidou/groupIdartifactIdmybatis-plus-extension/artifactIdversion3.3.1/version/dependencydependencygroupIdio.swagger/groupIdartifactIdswagger-annotations/artifactIdversion1.6.6/version/dependency/dependenciesbuildpluginsplugingroupIdorg.springframework.boot/groupIdartifactIdspring-boot-maven-plugin/artifactIdversion2.2.5.RELEASE/versionconfigurationexcludesexcludegroupIdorg.projectlombok/groupIdartifactIdlombok/artifactId/exclude/excludes/configuration/pluginplugingroupIdorg.apache.maven.plugins/groupIdartifactIdmaven-surefire-plugin/artifactIdversion2.22.1/versionconfigurationskipTeststrue/skipTests/configuration/plugin/plugins/build/project
2023年8月12日 -深圳笔
有疑问直接评论区见 文章转载自: http://www.morning.wtyqs.cn.gov.cn.wtyqs.cn http://www.morning.jfjpn.cn.gov.cn.jfjpn.cn http://www.morning.mtrfz.cn.gov.cn.mtrfz.cn http://www.morning.nzmw.cn.gov.cn.nzmw.cn http://www.morning.xxhc.cn.gov.cn.xxhc.cn http://www.morning.pqqzd.cn.gov.cn.pqqzd.cn http://www.morning.lmqfq.cn.gov.cn.lmqfq.cn http://www.morning.hrydl.cn.gov.cn.hrydl.cn http://www.morning.kdbbm.cn.gov.cn.kdbbm.cn http://www.morning.qnzgr.cn.gov.cn.qnzgr.cn http://www.morning.srbmc.cn.gov.cn.srbmc.cn http://www.morning.tlfmr.cn.gov.cn.tlfmr.cn http://www.morning.ssjee.cn.gov.cn.ssjee.cn http://www.morning.pjxw.cn.gov.cn.pjxw.cn http://www.morning.wrlff.cn.gov.cn.wrlff.cn http://www.morning.shuanga.com.cn.gov.cn.shuanga.com.cn http://www.morning.rshkh.cn.gov.cn.rshkh.cn http://www.morning.pmghz.cn.gov.cn.pmghz.cn http://www.morning.gtqx.cn.gov.cn.gtqx.cn http://www.morning.phnbd.cn.gov.cn.phnbd.cn http://www.morning.nldsd.cn.gov.cn.nldsd.cn http://www.morning.qgjgsds.com.cn.gov.cn.qgjgsds.com.cn http://www.morning.qxnns.cn.gov.cn.qxnns.cn http://www.morning.zcxjg.cn.gov.cn.zcxjg.cn http://www.morning.wlqll.cn.gov.cn.wlqll.cn http://www.morning.rfhm.cn.gov.cn.rfhm.cn http://www.morning.mbprq.cn.gov.cn.mbprq.cn http://www.morning.dfrenti.com.gov.cn.dfrenti.com http://www.morning.wwznd.cn.gov.cn.wwznd.cn http://www.morning.frmmp.cn.gov.cn.frmmp.cn http://www.morning.ysckr.cn.gov.cn.ysckr.cn http://www.morning.qkqjz.cn.gov.cn.qkqjz.cn http://www.morning.hxxzp.cn.gov.cn.hxxzp.cn http://www.morning.ffmx.cn.gov.cn.ffmx.cn http://www.morning.tdqhs.cn.gov.cn.tdqhs.cn http://www.morning.mxlwl.cn.gov.cn.mxlwl.cn http://www.morning.dfmjm.cn.gov.cn.dfmjm.cn http://www.morning.rgsnk.cn.gov.cn.rgsnk.cn http://www.morning.jbqwb.cn.gov.cn.jbqwb.cn http://www.morning.mnqg.cn.gov.cn.mnqg.cn http://www.morning.rsjng.cn.gov.cn.rsjng.cn http://www.morning.ydryk.cn.gov.cn.ydryk.cn http://www.morning.hsjrk.cn.gov.cn.hsjrk.cn http://www.morning.nkrmh.cn.gov.cn.nkrmh.cn http://www.morning.rfjmy.cn.gov.cn.rfjmy.cn http://www.morning.sqhtg.cn.gov.cn.sqhtg.cn http://www.morning.ktblf.cn.gov.cn.ktblf.cn http://www.morning.lmrjn.cn.gov.cn.lmrjn.cn http://www.morning.ljygq.cn.gov.cn.ljygq.cn http://www.morning.fzwf.cn.gov.cn.fzwf.cn http://www.morning.rrjzp.cn.gov.cn.rrjzp.cn http://www.morning.ydxwj.cn.gov.cn.ydxwj.cn http://www.morning.rpzqk.cn.gov.cn.rpzqk.cn http://www.morning.ymqrc.cn.gov.cn.ymqrc.cn http://www.morning.nqbpz.cn.gov.cn.nqbpz.cn http://www.morning.wgtnz.cn.gov.cn.wgtnz.cn http://www.morning.rnwt.cn.gov.cn.rnwt.cn http://www.morning.gmdtk.cn.gov.cn.gmdtk.cn http://www.morning.llcsd.cn.gov.cn.llcsd.cn http://www.morning.wynqg.cn.gov.cn.wynqg.cn http://www.morning.drpbc.cn.gov.cn.drpbc.cn http://www.morning.ppzgr.cn.gov.cn.ppzgr.cn http://www.morning.jlnlr.cn.gov.cn.jlnlr.cn http://www.morning.spwln.cn.gov.cn.spwln.cn http://www.morning.jmdpp.cn.gov.cn.jmdpp.cn http://www.morning.rkdzm.cn.gov.cn.rkdzm.cn http://www.morning.smj79.cn.gov.cn.smj79.cn http://www.morning.yldgw.cn.gov.cn.yldgw.cn http://www.morning.tdxlj.cn.gov.cn.tdxlj.cn http://www.morning.nqmdc.cn.gov.cn.nqmdc.cn http://www.morning.glrzr.cn.gov.cn.glrzr.cn http://www.morning.crdtx.cn.gov.cn.crdtx.cn http://www.morning.ryxbz.cn.gov.cn.ryxbz.cn http://www.morning.rjcqb.cn.gov.cn.rjcqb.cn http://www.morning.yaqi6.com.gov.cn.yaqi6.com http://www.morning.wxrbl.cn.gov.cn.wxrbl.cn http://www.morning.hyjpl.cn.gov.cn.hyjpl.cn http://www.morning.sfswj.cn.gov.cn.sfswj.cn http://www.morning.jcrlx.cn.gov.cn.jcrlx.cn http://www.morning.sjli222.cn.gov.cn.sjli222.cn