SpringBoot 图书管理系统

文章目录

  • 一、删除图书
  • 二、批量删除
  • 三、强制登录
    • 3.1 不使用拦截器
    • 3.2 使用拦截器
  • 四、更新图书

一、删除图书

  1. 并不使用delete语句
    • 原因:企业开发中,因为数据就意味着金钱,所以我们不会使用delete去删除(delete删除是物理删除,找不回来那种)
    • delete使用场景:delete 语句通常在进行数据修复时才会使用,比如测试人员如果要进行测试,是需要手工造一些数据的。当测试完毕后,这些数据就是脏数据(假数据)了,是没有任何价值的,此时就可以使用delete把数据删掉
  2. 删除的分类:逻辑删除 + 物理删除
    • 物理删除:直接把数据删掉
    • 逻辑删除:软删除/假删除,通过字段的标识来表示这个数据被删除了
      • 很多地方都在使用,比如软件开发、硬盘删除上
      • 删除时不是说把这块内容给清空了,而是把标识改了,这也是我们可以找回的原因(再把标识改回来)
    • 物理删除+存档 或 逻辑删除+存档
      • 什么是存档:
        • 建一个和原表字段一致的表 ,如果原表要删除一个数据,就把这个数据移到存档表里
        • 存档表我们大多数情况不使用,查找时是从原表里查,当需要数据恢复时,才会从存档表里查
      • 为什么都使用逻辑删除了,还要使用存档表:存档表可以认为是一个流水表
  3. 删除图书功能的实现方法:因为我们此处我们使用逻辑删除,所以没必要搞一个deleteBook,沿用修改图书的接口updateBook即可
  4. 代码:由于后端代码updateBook已经实现了,所以此处只要写前端代码即可(把id传过去,status固定为0)
function deleteBook(bookId){var isDelete = confirm("确认删除?");if (isDelete){$.ajax({type: "post",url: "/book/updateBook",data: {id: bookId,status: 0  //接口设计中,0表示被删除的},success: function (result){if (result == ""){location = "book_list.html";}else{alert(result);}}});}
}

二、批量删除

  1. 思路解析:因为我们使用了逻辑删除,所以批量删除就等于批量更新,但我们不能像删除单个图书一样,使用updateBook,因为此处我们需要更新多个,updateBook只能更新一个
  2. 后端代码
    Controller 层
    • @RequestParam:因为我们使用下面的方式发请求,参数是在查询字符串上。且后端设置的接口是List,默认接收是用数组(如果使用的是数组,可以不加该注解),所以要使用@RequestParam

      • @RequestParam:请求参数是查询字符串上的参数
      • @RequestBody:请求参数是body正文,需要把请求正文的内容转换为对象
      • @ResponseBody:返回的内容是响应正文
    • @RequestParam:关于参数的设计

      • 我们可以使用各种方法去接收前端发来的参数,可以设置参数在正文、url……里,接收的是数组、List、其他类……只要合乎逻辑,能完成需求即可

在这里插入图片描述

@RequestMapping("/batchDelete")
public String batchDeleteBook(@RequestParam List<Integer> ids){log.info("接收到的ids:{}", ids);Integer res = bookService.batchDeleteBook(ids);if (res <= 0){log.error("批量删除失败,ids:{}", ids);return "失败";}return "";
}

Service 层

  • try-catch:bookInfo.batchDelete(ids)为【主逻辑方法】,但是代码有可能会执行失败,所以要【try-catch】
  • 关于日志的打印
    • 可以方便我们后续找错,因为会有【需求没有实现 + 一条日志都没有的情况】,如SQL正确运行了,但影响的行数为0
    • 此时如果要排除错误,就需要一点一点debug,十分麻烦。有了日志,可以快速定位是哪里有问题
public Integer batchDeleteBook(@RequestParam List<Integer> ids){Integer res = null;try{res = bookInfoMapper.batchDelete(ids);}catch (Exception e){log.error("批量删除失败, ids:{}", ids);}return res;
}

Mapper 层

  • 因为会涉及到动态SQL,为了方便,此处使用xml来编写
Integer batchDelete(List<Integer> ids);
<update id="batchDelete">update book_infoset status = 0where id in<foreach collection="ids" item="id" open="(" close=")" separator=",">#{id}</foreach>
</update>
  1. 前端代码
    • input:checkbox:获取所有的复选框
    • name=‘selectBook’:复选框有很多,此时我们要获取名字为selectBook的
    • checked:表示已经被选中的
    • each(function):对每一个选中的复选框进行某个操作
    • ids.push($(this).val()):把复选框的值放到ids这个数组里
function batchDelete(){var isDelete = confirm("确认批量删除?");if (isDelete){var ids = [];$("input:checkbox[name='selectBook']:checked").each(function (){ids.push($(this).val());})$.ajax({type: "post",url: "/book/batchDelete?ids=" + ids,success:function (result){if (result == ""){location.href = "book_list.html";}else{alert(result);}}});}
}

三、强制登录

3.1 不使用拦截器

  1. 需求介绍:我们希望后端能检查有无登录,如果没有登录跳转到登录页面的操作
  2. 新增业务状态码和错误信息
    • 业务状态码:用来表示后端是否正确响应了,和http状态码是两个概念(业务状态码表示的是业务的情况,http状态码则是连接的情况,http请求成功才可能有业务状态码)
      • 案例:如果业务状态码表示成功,但是返回的数据为0,表示当前没有数据。如果业务状态码表示失败,数据也为0,此时就表示后端请求失败了
      • 数字的定义:业务状态码由程序员自定义(什么数字是什么情况),不过,我们一般会把失败定义为的数,把成功定义为>0的数
      • 前端的情况:哪怕业务状态码返回的那个数字表示的是失败,依旧是http成功连接的情况,前端走的是【success:funcation】,如果http状态码表示的是失败,走的是【error:function】
    • 错误信息:根据code知道具体错误是什么,然后前端把这个具体错误反馈给用户
@Data
public class PageResult<T> {private List<BookInfo> records;private Integer total;//此处设置0表示成功,-1为失败private Integer code;//错误信息private String errMsg;private PageRequest request;public PageResult(List<BookInfo> records, Integer total, PageRequest request) {this.records = records;this.total = total;this.request = request;}
}
  1. 新增Result类
    • 原因
      • 其他接口都需要强制登录等统一操作,如果都写在代码里,要写很多份,而且一旦要改需求,改动量大,耦合性太高
      • 故我们可以把之前的返回结果进行一个封装,封装为Result类,即每一个Controller接口返回的数据都是Result类,前端根据Result里的信息进行不同的反馈
        在这里插入图片描述
    • 优化tip ---->使用枚举
      • 原因:与其状态码这边使用Integer,还不如使用枚举,因为使用Integer还需要我们专门去查文档来看各个数字的是什么意思
      • Getter 和 Setter:因为枚举是不能用@Data的,所以此处我们要自己写Getter和Setter方法
public enum ResultCode {SUCCESS(0),FAIL(-1),UNLOGIN(-2);private int code;ResultCode(int code) {this.code = code;}public int getCode() {return code;}public void setCode(int code) {this.code = code;}
}
  1. 提取Session
    • 原因
      此时的Session是通过【session.setAttribute(“session_user_key”,userInfo)】存在【session_user_key】,但这依然有耦合性太高的可能性,而且是个【硬编码问题】,所以我们可以把他提取为一个常量
    • 使用方法
      在这里插入图片描述
    • 其他:关于硬编码问题 ----> 我们要避免字符串直接出现在代码中
      • 如果这个常量在很多地方要用,那就提到一个专门用来放常量的类里
      • 如果这个常量,只在当前这个类中使用,也可以通过【private static final xxx = xxx】的提上去
  2. 代码
    • new Result()
      在这里插入图片描述
    • 提取成构造函数
      在这里插入图片描述
      在这里插入图片描述
    • 优化tip:使用泛型Result<>
      • Obejct的时机:方法里我们用的是static,表示【静态】,静态的执行时机是要比类早的,但是泛型是要类执行了才能拿到类型是什么。所以当前最多给成员属性设置为泛型,方法中还是要使用Object
      • 代码:需要在方法里面再声明一下
@Data
public class Result<T> {/*** 业务状态码*/private ResultCode code;/*** 错误信息*/private String errMsg;/*** 把所有的返回数据都塞到这里*/private T data;/*** 成功时执行的方法* @return*/public static <T> Result<T> seccess(Object data){Result result = new Result();result.setCode(ResultCode.SUCCESS);result.setErrMsg("");result.setData(data);return result;}/*** 失败时执行的方法* 有错误,无数据* @param errMsg* @return*/public static <T> Result<T> fail(String errMsg){Result result = new Result();result.setCode(ResultCode.FAIL);result.setErrMsg(errMsg);result.setData(null);return result;}/*** 失败时执行的方法* 有错误,有* @param data* @param errMsg* @return*/public static <T> Result<T> fail(Object data, String errMsg){Result result = new Result();result.setCode(ResultCode.FAIL);result.setErrMsg(errMsg);result.setData(data);return result;}/*** 未登录时执行的方法* @return*/public static <T> Result<T> unlogin(){Result result = new Result();result.setCode(ResultCode.UNLOGIN);result.setErrMsg("用户未登录");result.setData(null);return result;}
}
  1. 测试
    • 需要先登录才能访问到列表页面
    • 如果已经登陆了,但是修改sessionId的值,依旧需要重新登录(找不到服务器里对应的session了)
      在这里插入图片描述

3.2 使用拦截器

  1. 关于写法:写法有很多,重点是实现需求
  2. response.setStatus(401):设置返回的http状态码为401
    • 401:未认证登录,或者提供的认证没被认可
    • 业务状态码由开发人员自定义,http状态码其实也是由http开发人员自定义的,但现在这已经成为了易总规范,大家都需要遵守
  3. 后端代码
    • response.setStatus(401):设置返回的http状态码为401
      • 401:未认证登录,或者提供的认证没被认可
      • 业务状态码由开发人员自定义,http状态码其实也是由http开发人员自定义的,但现在这已经成为了易总规范,大家都需要遵守
@Component
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {log.info("用户登录校验开始");HttpSession session = request.getSession(false);if (session != null && session.getAttribute(Constants.SESSION_USER_KEY) != null){UserInfo userInfo = (UserInfo) session.getAttribute(Constants.SESSION_USER_KEY);if (userInfo != null && userInfo.getId() > 0){return true;}}response.setStatus(401);return false;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {log.info("目标方法执行后");}}
@Configuration
public class webConfig implements WebMvcConfigurer {@Autowiredprivate LoginInterceptor loginInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(loginInterceptor).addPathPatterns("/**").excludePathPatterns("/user/login");  //在执行登录操作时,不要拦截}
}

在这里插入图片描述
4. 前端代码

  • 如果前端出现了错误,可以一行行注掉代码后,通过console.log打印日志来判断错误在哪
//http连接失败时执行的方法
error: function (error) {console.log(error);if (error.status == 401) {console.log("401");location.href = "login.html";}
}

四、更新图书

  1. 后端代码
    Controller层
@RequestMapping("/updateBook")
public String updateBook(BookInfo bookInfo){log.info("接收到的bookInfo:{}", bookInfo);Integer result = bookService.updateBook(bookInfo);if (result == 0){log.error("更新图书失败,请联系管理员");return "失败";}return "";
}

Service层

public Integer updateBook(BookInfo bookInfo){Integer res = 0;try{res = bookInfoMapper.updateBook(bookInfo);}catch (Exception e){log.error("更新图书失败,e:{}", e);}return res;
}

Mapper层

<?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_test.Mapper.BookInfoMapper"><update id="updateBook">update book_info<set><if test="bookName != null">book_name = #{bookName},</if><if test="author != null">author = #{author},</if><if test="count != null">count = #{count},</if><if test="price != null">price = #{price},</if><if test="publish != null">publish = #{publish},</if><if test="status != null">status = #{status}</if></set>where id = #{id};</update>
</mapper>
  1. 前端代码
<script>$.ajax({type: "get",url: "/book/queryBookInfoById" + location.search,success: function (book){if (book != null) {//页面输入框的填充$("#bookId").val(book.id);$("#bookName").val(book.bookName);$("#bookAuthor").val(book.author);$("#bookStock").val(book.count);$("#bookPrice").val(book.price);$("#bookPublisher").val(book.publish);$("#bookStatus").val(book.status)} else {alert("图书不存在")}}});function update() {$.ajax({type: "post",url: "/book/updateBook",data: $("#updateBook").serialize(),success: function (result) {if (result != null) {location.href = "book_list.html";} else {alert(result);}}});}
</script>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/1536667.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

基于SpringBoot的人事管理系统【附源码】

基于SpringBoot的人事管理系统&#xff08;源码L文说明文档&#xff09; 目录 4 系统设计 4.1 系统概述 4.2系统功能结构设计 4.3数据库设计 4.3.1数据库E-R图设计 4.3.2 数据库表结构设计 5 系统实现 5.1管理员功能介绍 5.1.1管理员登…

2分钟解决联想电脑wifi功能消失 网络适配器错误代码56

分钟解决联想电脑wifi功能消失 网络适配器错误代码56 现象 原因 电脑装了虚拟机&#xff0c;导致网络适配器冲突。我的电脑是装了vm虚拟机&#xff0c;上次更新系统后wifi图标就消失了。 解决方案 1、先卸载虚拟机 2、键盘按winr&#xff0c;弹出运行窗口&#xff0c;输入“…

LLVM PASS-PWN-前置

文章目录 参考环境搭建基础知识![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/dced705dcbb045ceb8df2237c9b0fd71.png)LLVM IR实例1. **.ll 格式&#xff08;人类可读的文本格式&#xff09;**2. **.bc 格式&#xff08;二进制格式&#xff09;**3. **内存表示** …

『功能项目』伤害数字UI显示【53】

我们打开上一篇52眩晕图标显示的项目&#xff0c; 本章要做的事情是在Boss受到伤害时显示伤害数字 首先打开Boss01预制体空间在Canvas下创建一个Text文本 设置Text文本 重命名为DamageUI 设置为隐藏 编写脚本&#xff1a;PlayerCtrl.cs 运行项目 本章做了怪物受伤血量的显示UI…

C语言 ——— 写一个宏,将一个整数的二进制位的奇数位和偶数位交换

目录 题目要求 代码实现 题目要求 写一个宏&#xff0c;将一个整数的二进制位的奇数位和偶数位交换 举例说明&#xff1a; 输入&#xff1a;10 10 的二进制为 1010 &#xff0c;奇数位和偶数位交换后得 0101 &#xff0c;也就是 5 输出&#xff1a;5 代码实现 代码演示&…

RK3568驱动指南|第十六篇 SPI-第190章 配置模式下寄存器的配置

瑞芯微RK3568芯片是一款定位中高端的通用型SOC&#xff0c;采用22nm制程工艺&#xff0c;搭载一颗四核Cortex-A55处理器和Mali G52 2EE 图形处理器。RK3568 支持4K 解码和 1080P 编码&#xff0c;支持SATA/PCIE/USB3.0 外围接口。RK3568内置独立NPU&#xff0c;可用于轻量级人工…

电流镜与恒流源

在两个晶体管完全对称的情况下&#xff0c;电源通过R1给两个晶体管提供相同的偏置电流&#xff0c; 这样他们流过集电极和发射极的电流就相同。 电流镜原视频链接&#xff1a; 【电流镜电路】https://www.bilibili.com/video/BV1b5411k7rh?vd_source3cc3c07b09206097d0d8b0ae…

Linux基础3-基础工具3(make,makefile,gdb详解)

上篇文章&#xff1a;Linux基础3-基础工具2&#xff08;vim详解&#xff0c;gcc详解&#xff09;-CSDN博客 本章重点&#xff1a; 1.自动化构建工具make,makefile 2.linux调试工具gdb 目录 一. 自动化构建工具make,makefile 1.1 make使用 1.2 使用make注意点 a. make和文件时…

Python数据分析案例60——扩展变量后的神经网络风速预测(tsfresh)

案例背景 时间序列的预测一直是经久不衰的实际应用和学术研究的对象&#xff0c;但是绝大多数的时间序列可能就没有太多的其他的变量&#xff0c;例如一个股票的股价&#xff0c;还有一个企业的用电量&#xff0c;人的血糖浓度等等&#xff0c;空气的质量&#xff0c;温度这些…

揭秘LLM计算数字的障碍的底层原理

LLM的 Tokenizer与数字切分 大语言模型在处理语言时&#xff0c;通常依赖Tokenization技术来将文本切分为可操作的单元。早期版本的Tokenizer对数字处理不够精确&#xff0c;常常将多个连续数字合并为一个Token。比如“13579”可能被切分为“13”、“57”和“9”。在这种情况…

【Linux修行路】网络套接字编程——UDP

目录 ⛳️推荐 前言 六、Udp Server 端代码 6.1 socket——创建套接字 6.2 bind——将套接字与一个 IP 和端口号进行绑定 6.3 recvfrom——从服务器的套接字里读取数据 6.4 sendto——向指定套接字中发送数据 6.5 绑定 ip 和端口号时的注意事项 6.5.1 云服务器禁止直接…

AIGC图片相关知识和实战经验(Flux.1,ComfyUI等等)

最近看了网上的一些新闻&#xff0c;flux.1火出圈了&#xff0c;因此自己也尝试跑了一下&#xff0c;作图的质量还是蛮高的&#xff0c;在这里做个知识总结回顾。 flux.1是什么&#xff1f; 根据介绍&#xff0c;flux.1是由stable diffusion 一作&#xff0c;Stability AI的核…

数据结构----栈和队列

&#xff08;一&#xff09;栈 1.栈的概念及结构 栈&#xff1a;一种特殊的线性表&#xff0c;其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端 称为栈顶&#xff0c;另一端称为栈底。栈中的数据元素遵守后进先出LIFO&#xff08;Last In First …

【数据结构】十大经典排序算法总结与分析

文章目录 前言1. 十大经典排序算法分类2. 相关概念3. 十大经典算法总结4. 补充内容4.1 比较排序和非比较排序的区别4.2 稳定的算法就真的稳定了吗&#xff1f;4.3 稳定的意义4.4 时间复杂度的补充4.5 空间复杂度补充 结语 前言 排序算法是《数据结构与算法》中最基本的算法之一…

PHP Swoole实现简易聊天室,附加小程序端连接websocket简易代码

目录 用到的工具&#xff1a; PHP Swoole拓展 | PHP Redis拓展 | Redis 7 一、安装上述必要工具&#xff08;下面是以宝塔面板中操作为例&#xff09; 给PHP安装Swoole和Redis拓展&#xff1a; 安装Redis软件 二、创建websocket服务器文件"wss_server.php" 具…

19 MDIO 接口读写以太网PHY寄存器

以太网概述 以太网&#xff08;Ethernet&#xff09;是应用最普遍的局域网技术。IEEE组织的 IEEE 802.3标准制定了以太网的技术标准&#xff0c;它规定了包括物理层的连线、电子信号和介质访问层协议的内容。以太网凭借其成本低、通信速率高、抗干扰性强等优点被广泛应用在网络…

2024 RSTCONCTF re 部分wp

Unknown Architect DIE查看&#xff0c;RISC_V架构&#xff0c;直接交即可 Duke of the Kingdom 附件拖入jadx 比较简单。脚本 Keypad 附件拖入ida。一共四遍check&#xff0c;都比较简单 Pico-Cypher 文本编辑器打开附件 稍微问一问gpt&#xff0c;得知这是micropython&#x…

2024年【浙江省安全员-C证】考试试卷及浙江省安全员-C证模拟考试题库

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 浙江省安全员-C证考试试卷是安全生产模拟考试一点通总题库中生成的一套浙江省安全员-C证模拟考试题库&#xff0c;安全生产模拟考试一点通上浙江省安全员-C证作业手机同步练习。2024年【浙江省安全员-C证】考试试卷及…

PostMan使用变量

环境变量 使用场景 当测试过程中&#xff0c;我们需要对开发环境、测试环境、生产环境进行测试 不同的环境对应着不同的服务器&#xff0c;那么这个时候我们就可以使用环境变量来区分它们 避免切换测试环境后&#xff0c;需要大量的更改接口的url地址 全局变量 使用场景 当…

[Leetcode LCR 154][Medium]-复杂链表的复制-链表

目录 一、题目描述 二、整体思路 三、代码 一、题目描述 原题地址 二、整体思路 这道题难点在于如何处理random。因为涉及到的所有节点都在同一链表&#xff0c;因此可以在链表上利用复制-拆分的方法去做。 先在链表上把每个节点复制自身一次&#xff0c;相当于cur与cur.ne…