SpringBoot 圖書管理系統(tǒng)(刪除、強制登錄、更新圖書)詳細代碼
一、刪除圖書
1.并不使用delete語句:
- 原因:企業(yè)開發(fā)中,因為數(shù)據(jù)就意味著金錢,所以我們不會使用delete去刪除(delete刪除是物理刪除,找不回來那種)
- delete使用場景:delete 語句通常在進行數(shù)據(jù)修復時才會使用,比如測試人員如果要進行測試,是需要手工造一些數(shù)據(jù)的。當測試完畢后,這些數(shù)據(jù)就是臟數(shù)據(jù)(假數(shù)據(jù))了,是沒有任何價值的,此時就可
2.以使用delete把數(shù)據(jù)刪掉
- 刪除的分類:邏輯刪除 + 物理刪除
- 物理刪除:直接把數(shù)據(jù)刪掉
- 邏輯刪除:軟刪除/假刪除,通過字段的標識來表示這個數(shù)據(jù)被刪除了
- 很多地方都在使用,比如軟件開發(fā)、硬盤刪除上
- 刪除時不是說把這塊內(nèi)容給清空了,而是把標識改了,這也是我們可以找回的原因(再把標識改回來)
- 物理刪除+存檔 或 邏輯刪除+存檔:
- 什么是存檔:
- 建一個和原表字段一致的表 ,如果原表要刪除一個數(shù)據(jù),就把這個數(shù)據(jù)移到存檔表里
- 存檔表我們大多數(shù)情況不使用,查找時是從原表里查,當需要數(shù)據(jù)恢復時,才會從存檔表里查
- 為什么都使用邏輯刪除了,還要使用存檔表:存檔表可以認為是一個流水表
- 什么是存檔:
3.刪除圖書功能的實現(xiàn)方法:因為我們此處我們使用邏輯刪除,所以沒必要搞一個deleteBook,沿用修改圖書的接口updateBook即可
4.代碼:由于后端代碼updateBook已經(jīng)實現(xiàn)了,所以此處只要寫前端代碼即可(把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:因為我們使用下面的方式發(fā)請求,參數(shù)是在查詢字符串上。且后端設置的接口是List,默認接收是用數(shù)組(如果使用的是數(shù)組,可以不加該注解),所以要使用@RequestParam
- @RequestParam:請求參數(shù)是查詢字符串上的參數(shù)
- @RequestBody:請求參數(shù)是body正文,需要把請求正文的內(nèi)容轉(zhuǎn)換為對象
- @ResponseBody:返回的內(nèi)容是響應正文
- @RequestParam:關(guān)于參數(shù)的設計
- 我們可以使用各種方法去接收前端發(fā)來的參數(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)為【主邏輯方法】,但是代碼有可能會執(zhí)行失敗,所以要【try-catch】
- 關(guān)于日志的打?。?ul>
- 可以方便我們后續(xù)找錯,因為會有【需求沒有實現(xiàn) + 一條日志都沒有的情況】,如SQL正確運行了,但影響的行數(shù)為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 層
- 因為會涉及到動態(tài)SQL,為了方便,此處使用xml來編寫
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:獲取所有的復選框
- name=‘selectBook’:復選框有很多,此時我們要獲取名字為selectBook的
- checked:表示已經(jīng)被選中的
- each(function):對每一個選中的復選框進行某個操作
- ids.push($(this).val()):把復選框的值放到ids這個數(shù)組里
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 不使用攔截器
- 需求介紹:我們希望后端能檢查有無登錄,如果沒有登錄跳轉(zhuǎn)到登錄頁面的操作
- 新增業(yè)務狀態(tài)碼和錯誤信息:
- 業(yè)務狀態(tài)碼:用來表示后端是否正確響應了,和http狀態(tài)碼是兩個概念(業(yè)務狀態(tài)碼表示的是業(yè)務的情況,http狀態(tài)碼則是連接的情況,http請求成功才可能有業(yè)務狀態(tài)碼)
- 案例:如果業(yè)務狀態(tài)碼表示成功,但是返回的數(shù)據(jù)為0,表示當前沒有數(shù)據(jù)。如果業(yè)務狀態(tài)碼表示失敗,數(shù)據(jù)也為0,此時就表示后端請求失敗了
- 數(shù)字的定義:業(yè)務狀態(tài)碼由程序員自定義(什么數(shù)字是什么情況),不過,我們一般會把失敗定義為的數(shù),把成功定義為>0的數(shù)
- 前端的情況:哪怕業(yè)務狀態(tài)碼返回的那個數(shù)字表示的是失敗,依舊是http成功連接的情況,前端走的是【success:funcation】,如果http狀態(tài)碼表示的是失敗,走的是【error:function】
- 業(yè)務狀態(tài)碼:用來表示后端是否正確響應了,和http狀態(tài)碼是兩個概念(業(yè)務狀態(tài)碼表示的是業(yè)務的情況,http狀態(tài)碼則是連接的情況,http請求成功才可能有業(yè)務狀態(tài)碼)
- 錯誤信息:根據(jù)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; } }
3.新增Result類:
- 原因:
- 其他接口都需要強制登錄等統(tǒng)一操作,如果都寫在代碼里,要寫很多份,而且一旦要改需求,改動量大,耦合性太高
- 故我們可以把之前的返回結(jié)果進行一個封裝,封裝為Result類,即每一個Controller接口返回的數(shù)據(jù)都是Result類,前端根據(jù)Result里的信息進行不同的反饋
- 優(yōu)化tip ---->使用枚舉:
- 原因:與其狀態(tài)碼這邊使用Integer,還不如使用枚舉,因為使用Integer還需要我們專門去查文檔來看各個數(shù)字的是什么意思
- 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; } }
4.提取Session:
- 原因:
- 此時的Session是通過【session.setAttribute(“session_user_key”,userInfo)】存在【session_user_key】,但這依然有耦合性太高的可能性,而且是個【硬編碼問題】,所以我們可以把他提取為一個常量
- 使用方法:
- 其他:關(guān)于硬編碼問題 ----> 我們要避免字符串直接出現(xiàn)在代碼中
- 如果這個常量在很多地方要用,那就提到一個專門用來放常量的類里
- 如果這個常量,只在當前這個類中使用,也可以通過【private static final xxx = xxx】的提上去
提取成構(gòu)造函數(shù):
優(yōu)化tip:使用泛型Result<>
- Obejct的時機:方法里我們用的是static,表示【靜態(tài)】,靜態(tài)的執(zhí)行時機是要比類早的,但是泛型是要類執(zhí)行了才能拿到類型是什么。所以當前最多給成員屬性設置為泛型,方法中還是要使用Object
- 代碼:需要在方法里面再聲明一下
@Data public class Result<T> { /** * 業(yè)務狀態(tài)碼 */ private ResultCode code; /** * 錯誤信息 */ private String errMsg; /** * 把所有的返回數(shù)據(jù)都塞到這里 */ private T data; /** * 成功時執(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; } /** * 失敗時執(zhí)行的方法 * 有錯誤,無數(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; } /** * 失敗時執(zhí)行的方法 * 有錯誤,有 * @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; } /** * 未登錄時執(zhí)行的方法 * @return */ public static <T> Result<T> unlogin(){ Result result = new Result(); result.setCode(ResultCode.UNLOGIN); result.setErrMsg("用戶未登錄"); result.setData(null); return result; } }
測試
- 需要先登錄才能訪問到列表頁面
- 如果已經(jīng)登陸了,但是修改sessionId的值,依舊需要重新登錄(找不到服務器里對應的session了)
3.2 使用攔截器
- 關(guān)于寫法:寫法有很多,重點是實現(xiàn)需求
- response.setStatus(401):設置返回的http狀態(tài)碼為401
- 401:未認證登錄,或者提供的認證沒被認可
- 業(yè)務狀態(tài)碼由開發(fā)人員自定義,http狀態(tài)碼其實也是由http開發(fā)人員自定義的,但現(xiàn)在這已經(jīng)成為了易總規(guī)范,大家都需要遵守
- 后端代碼:
- response.setStatus(401):設置返回的http狀態(tài)碼為401
- 401:未認證登錄,或者提供的認證沒被認可
- 業(yè)務狀態(tài)碼由開發(fā)人員自定義,http狀態(tài)碼其實也是由http開發(fā)人員自定義的,但現(xiàn)在這已經(jīng)成為了易總規(guī)范,大家都需要遵守
- response.setStatus(401):設置返回的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("用戶登錄校驗開始"); 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("目標方法執(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í)行登錄操作時,不要攔截 } }
4. 前端代碼:
如果前端出現(xiàn)了錯誤,可以一行行注掉代碼后,通過console.log打印日志來判斷錯誤在哪
//http連接失敗時執(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("更新圖書失敗,請聯(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) { //頁面輸入框的填充 $("#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)(刪除、強制登錄、更新圖書)詳細代碼的文章就介紹到這了,更多相關(guān)SpringBoot 圖書管理系統(tǒng)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java字符串日期類Date和Calendar相互轉(zhuǎn)化及相關(guān)常用方法
Java語言的Calendar(日歷),Date(日期),和DateFormat(日期格式)組成了Java標準的一個基本但是非常重要的部分,下面這篇文章主要給大家介紹了關(guān)于java字符串日期類Date和Calendar相互轉(zhuǎn)化及相關(guān)常用方法的相關(guān)資料,需要的朋友可以參考下2023-12-12SpringBoot集成Swagger使用SpringSecurity控制訪問權(quán)限問題
這篇文章主要介紹了SpringBoot集成Swagger使用SpringSecurity控制訪問權(quán)限問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-05-05SpringBoot實現(xiàn)HTTP調(diào)用的7 種方式
本文主要介紹了SpringBoot實現(xiàn)HTTP調(diào)用的7 種方式,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2025-04-04SpringMVC @GetMapping注解路徑?jīng)_突問題解決
MD5對密碼進行加密存儲是常見的一種加密方式,本文主要介紹了Java雙重MD5加密實現(xiàn)安全登錄,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2022-07-07spring boot項目application.properties文件存放及使用介紹
這篇文章主要介紹了spring boot項目application.properties文件存放及使用介紹,我們的application.properties文件中會有很多敏感信息,大家在使用過程中要多加小心2021-06-06HttpMessageConverter報文信息轉(zhuǎn)換器的深入講解
在Spring中內(nèi)置了大量的HttpMessageConverter,通過請求頭信息中的MIME類型,選擇相應的HttpMessageConverter,這篇文章主要給大家介紹了關(guān)于HttpMessageConverter報文信息轉(zhuǎn)換器的相關(guān)資料,需要的朋友可以參考下2022-01-01