SpringBoot 圖書管理系統(tǒng)(刪除、強(qiáng)制登錄、更新圖書)詳細(xì)代碼
一、刪除圖書
1.并不使用delete語(yǔ)句:
- 原因:企業(yè)開(kāi)發(fā)中,因?yàn)閿?shù)據(jù)就意味著金錢,所以我們不會(huì)使用delete去刪除(delete刪除是物理刪除,找不回來(lái)那種)
- delete使用場(chǎng)景:delete 語(yǔ)句通常在進(jìn)行數(shù)據(jù)修復(fù)時(shí)才會(huì)使用,比如測(cè)試人員如果要進(jìn)行測(cè)試,是需要手工造一些數(shù)據(jù)的。當(dāng)測(cè)試完畢后,這些數(shù)據(jù)就是臟數(shù)據(jù)(假數(shù)據(jù))了,是沒(méi)有任何價(jià)值的,此時(shí)就可
2.以使用delete把數(shù)據(jù)刪掉
- 刪除的分類:邏輯刪除 + 物理刪除
- 物理刪除:直接把數(shù)據(jù)刪掉
- 邏輯刪除:軟刪除/假刪除,通過(guò)字段的標(biāo)識(shí)來(lái)表示這個(gè)數(shù)據(jù)被刪除了
- 很多地方都在使用,比如軟件開(kāi)發(fā)、硬盤刪除上
- 刪除時(shí)不是說(shuō)把這塊內(nèi)容給清空了,而是把標(biāo)識(shí)改了,這也是我們可以找回的原因(再把標(biāo)識(shí)改回來(lái))
- 物理刪除+存檔 或 邏輯刪除+存檔:
- 什么是存檔:
- 建一個(gè)和原表字段一致的表 ,如果原表要?jiǎng)h除一個(gè)數(shù)據(jù),就把這個(gè)數(shù)據(jù)移到存檔表里
- 存檔表我們大多數(shù)情況不使用,查找時(shí)是從原表里查,當(dāng)需要數(shù)據(jù)恢復(fù)時(shí),才會(huì)從存檔表里查
- 為什么都使用邏輯刪除了,還要使用存檔表:存檔表可以認(rèn)為是一個(gè)流水表
- 什么是存檔:
3.刪除圖書功能的實(shí)現(xiàn)方法:因?yàn)槲覀兇颂幬覀兪褂眠壿媱h除,所以沒(méi)必要搞一個(gè)deleteBook,沿用修改圖書的接口updateBook即可
4.代碼:由于后端代碼updateBook已經(jīng)實(shí)現(xiàn)了,所以此處只要寫前端代碼即可(把id傳過(guò)去,status固定為0)
function deleteBook(bookId){
var isDelete = confirm("確認(rèn)刪除?");
if (isDelete){
$.ajax({
type: "post",
url: "/book/updateBook",
data: {
id: bookId,
status: 0 //接口設(shè)計(jì)中,0表示被刪除的
},
success: function (result){
if (result == ""){
location = "book_list.html";
}else{
alert(result);
}
}
});
}
}二、批量刪除
1.思路解析:因?yàn)槲覀兪褂昧诉壿媱h除,所以批量刪除就等于批量更新,但我們不能像刪除單個(gè)圖書一樣,使用updateBook,因?yàn)榇颂幬覀冃枰露鄠€(gè),updateBook只能更新一個(gè)
2.后端代碼:
Controller 層
- @RequestParam:因?yàn)槲覀兪褂孟旅娴姆绞桨l(fā)請(qǐng)求,參數(shù)是在查詢字符串上。且后端設(shè)置的接口是List,默認(rèn)接收是用數(shù)組(如果使用的是數(shù)組,可以不加該注解),所以要使用@RequestParam
- @RequestParam:請(qǐng)求參數(shù)是查詢字符串上的參數(shù)
- @RequestBody:請(qǐng)求參數(shù)是body正文,需要把請(qǐng)求正文的內(nèi)容轉(zhuǎn)換為對(duì)象
- @ResponseBody:返回的內(nèi)容是響應(yīng)正文
- @RequestParam:關(guān)于參數(shù)的設(shè)計(jì)
- 我們可以使用各種方法去接收前端發(fā)來(lái)的參數(shù),可以設(shè)置參數(shù)在正文、url……里,接收的是數(shù)組、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)為【主邏輯方法】,但是代碼有可能會(huì)執(zhí)行失敗,所以要【try-catch】
- 關(guān)于日志的打印:
- 可以方便我們后續(xù)找錯(cuò),因?yàn)闀?huì)有【需求沒(méi)有實(shí)現(xiàn) + 一條日志都沒(méi)有的情況】,如SQL正確運(yùn)行了,但影響的行數(shù)為0
- 此時(shí)如果要排除錯(cuò)誤,就需要一點(diǎn)一點(diǎn)debug,十分麻煩。有了日志,可以快速定位是哪里有問(wèn)題
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 層
- 因?yàn)闀?huì)涉及到動(dòng)態(tài)SQL,為了方便,此處使用xml來(lái)編寫
Integer batchDelete(List<Integer> ids);
<update id="batchDelete">
update book_info
set status = 0
where id in
<foreach collection="ids" item="id" open="(" close=")" separator=",">
#{id}
</foreach>
</update>3.前端代碼:
- input:checkbox:獲取所有的復(fù)選框
- name=‘selectBook’:復(fù)選框有很多,此時(shí)我們要獲取名字為selectBook的
- checked:表示已經(jīng)被選中的
- each(function):對(duì)每一個(gè)選中的復(fù)選框進(jìn)行某個(gè)操作
- ids.push($(this).val()):把復(fù)選框的值放到ids這個(gè)數(shù)組里
function batchDelete(){
var isDelete = confirm("確認(rèn)批量刪除?");
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);
}
}
});
}
}三、強(qiáng)制登錄
3.1 不使用攔截器
- 需求介紹:我們希望后端能檢查有無(wú)登錄,如果沒(méi)有登錄跳轉(zhuǎn)到登錄頁(yè)面的操作
- 新增業(yè)務(wù)狀態(tài)碼和錯(cuò)誤信息:
- 業(yè)務(wù)狀態(tài)碼:用來(lái)表示后端是否正確響應(yīng)了,和http狀態(tài)碼是兩個(gè)概念(業(yè)務(wù)狀態(tài)碼表示的是業(yè)務(wù)的情況,http狀態(tài)碼則是連接的情況,http請(qǐng)求成功才可能有業(yè)務(wù)狀態(tài)碼)
- 案例:如果業(yè)務(wù)狀態(tài)碼表示成功,但是返回的數(shù)據(jù)為0,表示當(dāng)前沒(méi)有數(shù)據(jù)。如果業(yè)務(wù)狀態(tài)碼表示失敗,數(shù)據(jù)也為0,此時(shí)就表示后端請(qǐng)求失敗了
- 數(shù)字的定義:業(yè)務(wù)狀態(tài)碼由程序員自定義(什么數(shù)字是什么情況),不過(guò),我們一般會(huì)把失敗定義為的數(shù),把成功定義為>0的數(shù)
- 前端的情況:哪怕業(yè)務(wù)狀態(tài)碼返回的那個(gè)數(shù)字表示的是失敗,依舊是http成功連接的情況,前端走的是【success:funcation】,如果http狀態(tài)碼表示的是失敗,走的是【error:function】
- 業(yè)務(wù)狀態(tài)碼:用來(lái)表示后端是否正確響應(yīng)了,和http狀態(tài)碼是兩個(gè)概念(業(yè)務(wù)狀態(tài)碼表示的是業(yè)務(wù)的情況,http狀態(tài)碼則是連接的情況,http請(qǐng)求成功才可能有業(yè)務(wù)狀態(tài)碼)
- 錯(cuò)誤信息:根據(jù)code知道具體錯(cuò)誤是什么,然后前端把這個(gè)具體錯(cuò)誤反饋給用戶
@Data
public class PageResult<T> {
private List<BookInfo> records;
private Integer total;
//此處設(shè)置0表示成功,-1為失敗
private Integer code;
//錯(cuò)誤信息
private String errMsg;
private PageRequest request;
public PageResult(List<BookInfo> records, Integer total, PageRequest request) {
this.records = records;
this.total = total;
this.request = request;
}
}3.新增Result類:
- 原因:
- 其他接口都需要強(qiáng)制登錄等統(tǒng)一操作,如果都寫在代碼里,要寫很多份,而且一旦要改需求,改動(dòng)量大,耦合性太高
- 故我們可以把之前的返回結(jié)果進(jìn)行一個(gè)封裝,封裝為Result類,即每一個(gè)Controller接口返回的數(shù)據(jù)都是Result類,前端根據(jù)Result里的信息進(jìn)行不同的反饋

- 優(yōu)化tip ---->使用枚舉:
- 原因:與其狀態(tài)碼這邊使用Integer,還不如使用枚舉,因?yàn)槭褂肐nteger還需要我們專門去查文檔來(lái)看各個(gè)數(shù)字的是什么意思
- Getter 和 Setter:因?yàn)槊杜e是不能用@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;
}
}4.提取Session:
- 原因:
- 此時(shí)的Session是通過(guò)【session.setAttribute(“session_user_key”,userInfo)】存在【session_user_key】,但這依然有耦合性太高的可能性,而且是個(gè)【硬編碼問(wèn)題】,所以我們可以把他提取為一個(gè)常量
- 使用方法:

- 其他:關(guān)于硬編碼問(wèn)題 ----> 我們要避免字符串直接出現(xiàn)在代碼中
- 如果這個(gè)常量在很多地方要用,那就提到一個(gè)專門用來(lái)放常量的類里
- 如果這個(gè)常量,只在當(dāng)前這個(gè)類中使用,也可以通過(guò)【private static final xxx = xxx】的提上去

提取成構(gòu)造函數(shù):


優(yōu)化tip:使用泛型Result<>
- Obejct的時(shí)機(jī):方法里我們用的是static,表示【靜態(tài)】,靜態(tài)的執(zhí)行時(shí)機(jī)是要比類早的,但是泛型是要類執(zhí)行了才能拿到類型是什么。所以當(dāng)前最多給成員屬性設(shè)置為泛型,方法中還是要使用Object
- 代碼:需要在方法里面再聲明一下
@Data
public class Result<T> {
/**
* 業(yè)務(wù)狀態(tài)碼
*/
private ResultCode code;
/**
* 錯(cuò)誤信息
*/
private String errMsg;
/**
* 把所有的返回?cái)?shù)據(jù)都塞到這里
*/
private T data;
/**
* 成功時(shí)執(zhí)行的方法
* @return
*/
public static <T> Result<T> seccess(Object data){
Result result = new Result();
result.setCode(ResultCode.SUCCESS);
result.setErrMsg("");
result.setData(data);
return result;
}
/**
* 失敗時(shí)執(zhí)行的方法
* 有錯(cuò)誤,無(wú)數(shù)據(jù)
* @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;
}
/**
* 失敗時(shí)執(zhí)行的方法
* 有錯(cuò)誤,有
* @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;
}
/**
* 未登錄時(shí)執(zhí)行的方法
* @return
*/
public static <T> Result<T> unlogin(){
Result result = new Result();
result.setCode(ResultCode.UNLOGIN);
result.setErrMsg("用戶未登錄");
result.setData(null);
return result;
}
}測(cè)試
- 需要先登錄才能訪問(wèn)到列表頁(yè)面
- 如果已經(jīng)登陸了,但是修改sessionId的值,依舊需要重新登錄(找不到服務(wù)器里對(duì)應(yīng)的session了)

3.2 使用攔截器
- 關(guān)于寫法:寫法有很多,重點(diǎn)是實(shí)現(xiàn)需求
- response.setStatus(401):設(shè)置返回的http狀態(tài)碼為401
- 401:未認(rèn)證登錄,或者提供的認(rèn)證沒(méi)被認(rèn)可
- 業(yè)務(wù)狀態(tài)碼由開(kāi)發(fā)人員自定義,http狀態(tài)碼其實(shí)也是由http開(kāi)發(fā)人員自定義的,但現(xiàn)在這已經(jīng)成為了易總規(guī)范,大家都需要遵守
- 后端代碼:
- response.setStatus(401):設(shè)置返回的http狀態(tài)碼為401
- 401:未認(rèn)證登錄,或者提供的認(rèn)證沒(méi)被認(rèn)可
- 業(yè)務(wù)狀態(tài)碼由開(kāi)發(fā)人員自定義,http狀態(tài)碼其實(shí)也是由http開(kāi)發(fā)人員自定義的,但現(xiàn)在這已經(jīng)成為了易總規(guī)范,大家都需要遵守
- response.setStatus(401):設(shè)置返回的http狀態(tài)碼為401
@Component
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
log.info("用戶登錄校驗(yàn)開(kāi)始");
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;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
log.info("目標(biāo)方法執(zhí)行后");
}
}@Configuration
public class webConfig implements WebMvcConfigurer {
@Autowired
private LoginInterceptor loginInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginInterceptor)
.addPathPatterns("/**")
.excludePathPatterns("/user/login"); //在執(zhí)行登錄操作時(shí),不要攔截
}
}
4. 前端代碼:
如果前端出現(xiàn)了錯(cuò)誤,可以一行行注掉代碼后,通過(guò)console.log打印日志來(lái)判斷錯(cuò)誤在哪
//http連接失敗時(shí)執(zhí)行的方法
error: function (error) {
console.log(error);
if (error.status == 401) {
console.log("401");
location.href = "login.html";
}
}四、更新圖書
后端代碼:
Controller層
@RequestMapping("/updateBook")
public String updateBook(BookInfo bookInfo){
log.info("接收到的bookInfo:{}", bookInfo);
Integer result = bookService.updateBook(bookInfo);
if (result == 0){
log.error("更新圖書失敗,請(qǐng)聯(lián)系管理員");
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>前端代碼:
<script>
$.ajax({
type: "get",
url: "/book/queryBookInfoById" + location.search,
success: function (book){
if (book != null) {
//頁(yè)面輸入框的填充
$("#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>到此這篇關(guān)于SpringBoot 圖書管理系統(tǒng)(刪除、強(qiáng)制登錄、更新圖書)詳細(xì)代碼的文章就介紹到這了,更多相關(guān)SpringBoot 圖書管理系統(tǒng)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
使用mybatis-plus中Page進(jìn)行分頁(yè)不生效解決過(guò)程
在使用MyBatis-Plus的Page進(jìn)行分頁(yè)時(shí),如果發(fā)現(xiàn)分頁(yè)不生效,可能是由于未正確配置分頁(yè)插件,確保在配置類中正確引入了分頁(yè)插件,并且數(shù)據(jù)庫(kù)類型設(shè)置正確,同時(shí),檢查MybatisPlusConfig類是否被正確注入2025-12-12
詳解Alibaba?Java診斷工具Arthas查看Dubbo動(dòng)態(tài)代理類
這篇文章主要介紹了Alibaba?Java診斷工具Arthas查看Dubbo動(dòng)態(tài)代理類?,它可以幫助我們查看JDK或者javassist生成的動(dòng)態(tài)代理類,當(dāng)然,它的功能遠(yuǎn)不止此,還可以在生產(chǎn)環(huán)境進(jìn)行診斷,需要的朋友可以參考下2022-04-04
Spring TransactionalEventListener事務(wù)未提交讀取不到數(shù)據(jù)的解決
這篇文章主要介紹了Spring TransactionalEventListener事務(wù)未提交讀取不到數(shù)據(jù)的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-09-09
SpringCloud LoadBalancer負(fù)載均衡策略與緩存機(jī)制詳解
這篇文章主要介紹了SpringCloud LoadBalancer負(fù)載均衡策略與緩存機(jī)制詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2026-03-03
談?wù)勎覍?duì)Spring Bean 生命周期的理解
Spring Bean 的生命周期在整個(gè) Spring 中占有很重要的位置,掌握這些可以加深對(duì) Spring 的理解。這篇文章主要介紹了Spring Bean 生命周期,需要的朋友可以參考下2018-03-03
Spring負(fù)載均衡LoadBalancer使用詳解
這篇文章主要介紹了Spring負(fù)載均衡LoadBalancer使用詳解,Spring Cloud LoadBalancer是Spring Cloud官方自己提供的客戶端負(fù)載均衡器, 用來(lái)替代Ribbon,Spring官方提供了兩種客戶端都可以使用loadbalancer,需要的朋友可以參考下2023-11-11
讀寫nacos的配置中心的參數(shù)及變動(dòng)監(jiān)聽(tīng)方式
Spring應(yīng)用可以通過(guò)NacosDataSource下載配置參數(shù),使用ConfigService的publishConfig方法更新Nacos上的配置,但同一服務(wù)內(nèi)部更新配置不會(huì)觸發(fā)configUpdate事件,ConfigService也提供了監(jiān)聽(tīng)事件功能2026-02-02
SpringBoot靜態(tài)資源與首頁(yè)配置實(shí)現(xiàn)原理深入分析
最近在做SpringBoot項(xiàng)目的時(shí)候遇到了“白頁(yè)”問(wèn)題,通過(guò)查資料對(duì)SpringBoot訪問(wèn)靜態(tài)資源做了總結(jié),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)吧2022-10-10
解決springcloud中Feign導(dǎo)入依賴為unknow的情況
這篇文章主要介紹了解決springcloud中Feign導(dǎo)入依賴為unknow的情況,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03

