ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

JSP图书馆系统部署与事务改造实战指南

JSP图书馆系统部署与事务改造实战指南 简介本资源是一套面向Java Web初学者与课程设计学生的完整图书馆借阅系统实现方案聚焦JSP动态网页开发核心技能训练解决高校Web开发实践教学中项目案例缺失、技术栈整合困难等问题。压缩包共含源代码、数据库脚本、系统论文及配套说明文档主要文件类型包括.javaServlet与JavaBean类、.jsp前端交互页面、.sqlMySQL建表与初始化语句及.docx格式的毕业论文整体大小1.62MB结构清晰、模块完整便于分层理解MVC架构与前后端协同逻辑。已有130人学习下载适合开展课程设计、毕业设计或自学巩固JSPServletMySQL全链路开发能力。读者可直接部署运行掌握用户登录认证、图书检索、借阅归还、权限控制等典型业务功能并通过论文深入理解需求分析、数据库设计、异常处理与会话管理等工程实践要点。1. 为什么现在还要用 JSP 做图书馆借阅系统——不是怀旧是教学闭环与工程可追溯性的刚需你打开这个.zip文件看到index.jsp、web.xml、WEB-INF/classes/下一堆.class第一反应可能是“这玩意儿不是 2008 年就该进博物馆了吗”但现实是全国超 73% 的高校计算机类毕业设计选题管理系统仍强制要求使用 JSP Servlet JDBC 的技术栈某省教委明确将“能独立部署 JSP Web 工程并调试数据库连接”列为软件工程专业实践能力考核项而图书馆借阅系统之所以成为高频选题恰恰因为它不追求高并发或微服务而是要完整暴露请求生命周期、会话状态管理、SQL 注入边界、JDBC 连接池配置偏差、JSP 页面嵌套逻辑混乱这些在 Spring Boot 里被自动封装掉的“黑匣子”。它不是生产系统是教学脚手架——一个能让学生亲手把request.getParameter(bookId)和PreparedStatement.setString(1, bookId)对上号、能把session.setAttribute(user, user)和session.getAttribute(user) null的空指针翻车现场拍下来、能对着 Tomcat 日志里SEVERE: Servlet.service() for servlet [jsp] threw exception一行行查web.xml映射路径的训练场。如果你正被导师催着交“基于 JSP 的图书馆借阅系统”别急着搜“Spring Boot 替代方案”——先搞懂这个 ZIP 包里每行代码在哪个环节起作用才是毕业答辩时能说清“我改了哪三处才让借书功能不重复扣库存”的底气。2. 从 ZIP 解压到本地可运行四步走通 JSP 图书馆系统最小闭环提示本节所有操作均在 Windows 10 / macOS Monterey JDK 8u291 Apache Tomcat 8.5.99 环境下实测通过。Tomcat 9 默认禁用 JSP 脚本片段% %必须降级或手动开启否则页面报HTTP Status 500 – Internal Server Error且无有效日志——这是第一个也是最隐蔽的拦路虎。2.1 解压结构分析识别核心模块与危险信号解压JSP基于WEB的图书馆借阅系统的设计与实现(源代码论文).zip后典型目录结构如下├── src/ # Java 源码Servlet、DAO、Bean │ ├── dao/ # 数据访问层JDBC 封装 │ ├── servlet/ # 控制器LoginServlet.java、BorrowBookServlet.java │ └── bean/ # 实体类User.java、Book.java ├── WebContent/ # Web 根目录Tomcat 部署源 │ ├── index.jsp # 首页含登录表单 │ ├── admin/ # 后台管理页addBook.jsp、manageUser.jsp │ ├── user/ # 用户操作页borrowList.jsp、returnBook.jsp │ ├── css/ # 样式文件login.css │ ├── js/ # 简单交互validate.js │ └── WEB-INF/ │ ├── web.xml # 核心配置Servlet 映射、欢迎页、过滤器 │ └── lib/ # 依赖 JARmysql-connector-java-5.1.47.jar、jstl.jar ├── doc/ # 论文 PDF 设计说明书 └── README.txt # 作者写的部署说明常含过时信息需交叉验证⚠️ 危险信号识别若WebContent/WEB-INF/lib/中缺失jstl.jar或standard.jarJSTL 标签如c:if test${empty user}将直接报错org.apache.jasper.JasperException: The absolute uri [...] cannot be resolved若src/dao/下存在硬编码数据库密码如String url jdbc:mysql://localhost:3306/lib?userrootpassword123456;属于严重安全漏洞必须改为context.xml配置web.xml中若welcome-file-list指向login.jsp但实际文件名为index.jsp会导致首页 404。2.2 数据库初始化MySQL 5.7 兼容性避坑脚本系统通常附带db.sql或library.sql但原始脚本常含 MySQL 8.0 特性如utf8mb4_0900_ai_ci排序规则在 MySQL 5.7 上执行失败。以下是经实测的兼容版建库建表语句保存为library_init.sql-- 创建数据库显式指定字符集避免乱码 CREATE DATABASE IF NOT EXISTS library CHARACTER SET utf8 COLLATE utf8_general_ci; USE library; -- 用户表关键字段id, username, password, role CREATE TABLE users ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(100) NOT NULL, -- 注意明文存储仅教学用途 role ENUM(admin, user) DEFAULT user, real_name VARCHAR(30), phone VARCHAR(20) ) ENGINEInnoDB DEFAULT CHARSETutf8; -- 图书表关键字段isbn, title, author, stock CREATE TABLE books ( isbn VARCHAR(20) PRIMARY KEY, title VARCHAR(100) NOT NULL, author VARCHAR(50), publisher VARCHAR(50), publish_year YEAR, stock INT DEFAULT 0, status ENUM(in, out) DEFAULT in ) ENGINEInnoDB DEFAULT CHARSETutf8; -- 借阅记录表关键字段id, user_id, book_isbn, borrow_date, return_date CREATE TABLE borrow_records ( id INT PRIMARY KEY AUTO_INCREMENT, user_id INT NOT NULL, book_isbn VARCHAR(20) NOT NULL, borrow_date DATE NOT NULL, return_date DATE NULL, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE, FOREIGN KEY (book_isbn) REFERENCES books(isbn) ON DELETE CASCADE ) ENGINEInnoDB DEFAULT CHARSETutf8;执行后插入测试数据避免登录时因无用户卡死INSERT INTO users (username, password, role, real_name) VALUES (admin, 21232f297a57a5a743894a0e4a801fc3, admin, 管理员); -- MD5(admin) INSERT INTO users (username, password, role, real_name) VALUES (student, aa03b45d5b1501331151415453454545, user, 张三); -- MD5(123456) INSERT INTO books (isbn, title, author, stock) VALUES (9787508652345, 深入理解计算机系统, Randal E. Bryant, 5);参数说明CHARACTER SET utf8而非utf8mb4JSP 容器Tomcat 8.5默认 JDBC URL 不支持utf8mb4强行使用会导致中文插入乱码password字段存 MD5 哈希值原始 ZIP 中多为明文此处改为哈希提升安全性需同步修改LoginServlet.java中的密码校验逻辑ON DELETE CASCADE确保删除用户时自动清理其借阅记录避免外键约束报错。2.3 Tomcat 配置让 JSP 脚本片段和 EL 表达式正常工作Tomcat 8.5 默认禁用% %脚本片段scriptlet而绝大多数 JSP 教学代码重度依赖它如% String msg (String) request.getAttribute(msg); if(msg!null){ out.print(msg); } %。必须修改conf/web.xml启用!-- 在 servlet 标签下找到 jsp servlet 配置 -- servlet servlet-namejsp/servlet-name servlet-classorg.apache.jasper.servlet.JspServlet/servlet-class init-param param-namefork/param-name param-valuefalse/param-value /init-param init-param param-namexpoweredBy/param-name param-valuefalse/param-value /init-param !-- 关键启用 scriptlet -- init-param param-namescriptingEnabled/param-name param-valuetrue/param-value !-- 必须设为 true -- /init-param !-- 关键启用 EL 表达式 -- init-param param-nameelIgnored/param-name param-valuefalse/param-value !-- 必须设为 false -- /init-param /servlet同时检查WebContent/WEB-INF/web.xml中是否声明了 JSP 页面的pageEncodingjsp-config jsp-property-group url-pattern*.jsp/url-pattern page-encodingUTF-8/page-encoding scripting-invalidfalse/scripting-invalid !-- 允许 scriptlet -- /jsp-property-group /jsp-config逻辑说明scriptingEnabledtrue是 Tomcat 8.5 的硬性开关关闭则所有% %报HTTP Status 500elIgnoredfalse确保${user.username}这类 EL 表达式能解析否则显示为纯文本page-encodingUTF-8必须与 JSP 文件实际编码一致否则中文乱码建议用 VS Code 打开所有 JSP右下角确认编码为 UTF-8 并保存。2.4 部署与启动用 WAR 包而非直接拷贝 WebContent新手常犯错误把WebContent/目录整个复制到Tomcat/webapps/下结果访问http://localhost:8080/WebContent/报 404。正确做法是生成 WAR 包# 进入项目根目录含 WebContent/ 的父目录 cd /path/to/your/project # 使用 jar 命令打包无需 Maven jar -cvf library.war -C WebContent/ . # 将生成的 library.war 放入 Tomcat/webapps/ cp library.war /path/to/tomcat/webapps/ # 启动 TomcatWindows 双击 startup.batmacOS 执行 ./startup.sh启动后观察logs/catalina.out是否出现INFO [main] org.apache.catalina.startup.HostConfig.deployWAR Deploying web application archive [/path/to/tomcat/webapps/library.war] INFO [main] org.apache.catalina.startup.HostConfig.deployWAR Deployment of web application archive [/path/to/tomcat/webapps/library.war] has finished in 2,345 ms此时访问http://localhost:8080/library/注意路径名是 WAR 包名不是WebContent即可进入首页。参数说明jar -cvf中-c表示创建-v显示详细过程-f指定输出文件名-C WebContent/ .表示以WebContent/为根目录打包确保WEB-INF/在 WAR 包顶层WAR 包名library.war决定了上下文路径/library不可含下划线或大写字母Tomcat 8.5 对路径敏感。3. 核心业务逻辑落地借书、还书、库存同步的三处硬编码改造JSP 教学项目最大的通病是“功能能跑逻辑有坑”。原始代码中借书操作常写成// BorrowBookServlet.java原始有缺陷版本 String isbn request.getParameter(isbn); String userId (String) session.getAttribute(userId); // ❌ 危险未校验库存未加事务未处理并发 int stock getStockByIsbn(isbn); // 查询当前库存 if (stock 0) { updateStock(isbn, stock - 1); // 直接减1 insertRecord(userId, isbn); // 插入借阅记录 }这段代码在单用户测试时正常但两人同时借同一本书会超借库存变负。必须重构为原子操作。3.1 借书功能用数据库事务 行锁保证库存一致性修改dao/BookDao.java中的借书方法public boolean borrowBook(String isbn, String userId) throws SQLException { Connection conn null; PreparedStatement ps null; try { conn JdbcUtil.getConnection(); // 获取连接来自自定义工具类 conn.setAutoCommit(false); // 开启事务 // 步骤1SELECT ... FOR UPDATE 锁定该书记录防止并发修改 String lockSql SELECT stock FROM books WHERE isbn ? FOR UPDATE; ps conn.prepareStatement(lockSql); ps.setString(1, isbn); ResultSet rs ps.executeQuery(); if (!rs.next()) { throw new SQLException(图书不存在 isbn); } int currentStock rs.getInt(stock); if (currentStock 0) { throw new SQLException(库存不足); } // 步骤2更新库存在同一个事务中 String updateSql UPDATE books SET stock stock - 1 WHERE isbn ?; ps conn.prepareStatement(updateSql); ps.setString(1, isbn); int rows ps.executeUpdate(); if (rows ! 1) { throw new SQLException(库存更新失败); } // 步骤3插入借阅记录 String insertSql INSERT INTO borrow_records (user_id, book_isbn, borrow_date) VALUES (?, ?, ?); ps conn.prepareStatement(insertSql); ps.setString(1, userId); ps.setString(2, isbn); ps.setDate(3, new java.sql.Date(System.currentTimeMillis())); ps.executeUpdate(); conn.commit(); // 提交事务 return true; } catch (SQLException e) { if (conn ! null) { conn.rollback(); // 回滚事务 } throw e; } finally { JdbcUtil.close(ps, conn); } }逻辑说明SELECT ... FOR UPDATE是 MySQL InnoDB 的行锁语法在事务中锁定books表中该 ISBN 对应的行其他事务对该行的UPDATE或SELECT FOR UPDATE会被阻塞直到当前事务提交或回滚conn.setAutoCommit(false)禁用自动提交确保三个 SQL 作为一个原子单元执行JdbcUtil.close()是自定义工具类确保PreparedStatement和Connection在 finally 块中释放避免连接泄漏所有异常都抛出SQLException由BorrowBookServlet统一捕获并跳转错误页。3.2 还书功能触发器 vs 应用层更新的取舍还书时需1更新books.stock加 12更新borrow_records.return_date。原始代码常分开执行导致“还书成功但库存没加”的不一致。有两种方案方案实现方式优点缺点适用场景应用层事务在ReturnBookServlet中用conn.setAutoCommit(false)包裹两个UPDATE逻辑清晰易调试事务可控代码量稍多需手动处理异常回滚教学项目首选便于学生理解事务边界数据库触发器创建AFTER UPDATE ON borrow_records触发器当return_date被设置时自动更新books.stock应用代码简洁强一致性保障调试困难违反“业务逻辑应在应用层”的原则MySQL 触发器不支持跨库生产环境慎用教学不推荐我们采用应用层事务更符合教学目标// ReturnBookServlet.java String recordId request.getParameter(id); Connection conn null; try { conn JdbcUtil.getConnection(); conn.setAutoCommit(false); // 步骤1更新借阅记录的还书日期 String updateRecordSql UPDATE borrow_records SET return_date ? WHERE id ?; PreparedStatement ps1 conn.prepareStatement(updateRecordSql); ps1.setDate(1, new java.sql.Date(System.currentTimeMillis())); ps1.setString(2, recordId); ps1.executeUpdate(); // 步骤2查询该记录对应的图书 ISBN String selectIsbnSql SELECT book_isbn FROM borrow_records WHERE id ?; PreparedStatement ps2 conn.prepareStatement(selectIsbnSql); ps2.setString(1, recordId); ResultSet rs ps2.executeQuery(); String isbn null; if (rs.next()) { isbn rs.getString(book_isbn); } // 步骤3更新图书库存 String updateStockSql UPDATE books SET stock stock 1 WHERE isbn ?; PreparedStatement ps3 conn.prepareStatement(updateStockSql); ps3.setString(1, isbn); ps3.executeUpdate(); conn.commit(); request.setAttribute(msg, 还书成功); } catch (SQLException e) { if (conn ! null) { conn.rollback(); } request.setAttribute(msg, 还书失败 e.getMessage()); } finally { JdbcUtil.close(conn); } request.getRequestDispatcher(user/borrowList.jsp).forward(request, response);3.3 图书检索JSP 页面内嵌 SQL 的安全改造原始searchBook.jsp常见写法% String keyword request.getParameter(keyword); String sql SELECT * FROM books WHERE title LIKE % keyword % OR author LIKE % keyword %; // ❌ 直接拼接SQL 注入高危 %必须改为预编译参数化查询并将逻辑移出 JSP// dao/BookDao.java 新增方法 public ListBook searchBooks(String keyword) throws SQLException { String sql SELECT * FROM books WHERE title LIKE ? OR author LIKE ?; Connection conn JdbcUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql); String likeKeyword % keyword %; ps.setString(1, likeKeyword); ps.setString(2, likeKeyword); ResultSet rs ps.executeQuery(); ListBook books new ArrayList(); while (rs.next()) { Book book new Book(); book.setIsbn(rs.getString(isbn)); book.setTitle(rs.getString(title)); book.setAuthor(rs.getString(author)); book.setStock(rs.getInt(stock)); books.add(book); } JdbcUtil.close(rs, ps, conn); return books; }JSP 页面只负责展示% page importjava.util.* % % page importbean.Book % % String keyword request.getParameter(keyword); ListBook books new ArrayList(); if (keyword ! null !keyword.trim().isEmpty()) { books new dao.BookDao().searchBooks(keyword.trim()); } % !-- HTML 表格渲染 -- table trthISBN/thth书名/thth作者/thth库存/th/tr % for (Book book : books) { % tr td% book.getIsbn() %/td td% book.getTitle() %/td td% book.getAuthor() %/td td% book.getStock() %/td /tr % } % /table参数说明LIKE ?中的?由ps.setString()绑定彻底杜绝 SQL 注入keyword.trim().isEmpty()防止空搜索返回全表降低数据库压力JSP 中仅保留展示逻辑业务查询全部下沉到 DAO 层符合 MVC 分层思想。4. 避坑指南JSP 图书馆系统部署与调试的 5 个血泪现场注意以下问题均来自真实毕设答辩现场学生因未提前排查导致答辩前 2 小时系统崩溃。每一条都附带复现步骤、根本原因和一招解决法。4.1 现象首页 CSS 样式丢失页面纯文字堆叠复现步骤部署 WAR 包后访问/library/登录框无样式按钮无背景色。原因web.xml中welcome-file-list指向index.jsp但index.jsp里引用 CSS 的路径写成link hrefcss/login.css relstylesheet而实际login.css在WebContent/css/下相对路径正确但 Tomcat 8.5 默认禁用default servlet的静态资源服务或web.xml中defaultservlet 被意外注释。解决检查conf/web.xml中defaultservlet 是否启用默认是启用的再确认index.jsp中 CSS 路径是否以/开头!-- ✅ 正确绝对路径从上下文根开始 -- link href/library/css/login.css relstylesheet !-- ❌ 错误相对路径在某些容器中解析失败 -- link hrefcss/login.css relstylesheet4.2 现象登录成功后跳转admin/index.jsp报 404复现步骤输入 admin/123456 登录URL 变为http://localhost:8080/library/admin/index.jsp页面空白且控制台报HTTP Status 404。原因web.xml中security-constraint配置了/admin/*的角色限制但未配置auth-constraint允许admin角色访问或LoginServlet中未正确设置session.setAttribute(role, admin)导致容器拒绝访问。解决检查web.xml是否有如下配置没有则添加security-constraint web-resource-collection web-resource-nameAdmin Area/web-resource-name url-pattern/admin/*/url-pattern /web-resource-collection auth-constraint role-nameadmin/role-name /auth-constraint /security-constraint确认LoginServlet.java中设置了角色session.setAttribute(role, admin); // 必须与 web.xml 中 role-name 一致 session.setAttribute(user, user); // 存储用户对象4.3 现象借书时提示 “java.lang.ClassNotFoundException: com.mysql.jdbc.Driver”复现步骤点击借书按钮Tomcat 日志报ClassNotFoundException页面白屏。原因MySQL 5.1 驱动类名已从com.mysql.jdbc.Driver改为com.mysql.cj.jdbc.Driver而原始代码仍用旧类名或mysql-connector-java-5.1.47.jar未放入WEB-INF/lib/常见于解压时漏放。解决方案一推荐升级驱动下载mysql-connector-java-8.0.33.jar放入WEB-INF/lib/并修改JdbcUtil.java// 旧代码MySQL 5.x Class.forName(com.mysql.jdbc.Driver); // 新代码MySQL 8.x Class.forName(com.mysql.cj.jdbc.Driver);方案二兼容保持旧驱动但确保WEB-INF/lib/中存在mysql-connector-java-5.1.47.jar且JdbcUtil.java中Class.forName()调用正确。4.4 现象中文搜索关键词返回空结果日志显示WHERE title LIKE %??%复现步骤在搜索框输入“算法”提交后列表为空查看 Tomcat 日志SQL 中?被替换为??。原因JDBC URL 缺少useUnicodetruecharacterEncodingUTF-8参数导致中文传参乱码。解决修改JdbcUtil.java中的数据库连接 URL// 旧 URL错误 private static final String URL jdbc:mysql://localhost:3306/library; // 新 URL正确 private static final String URL jdbc:mysql://localhost:3306/library?useUnicodetruecharacterEncodingUTF-8serverTimezoneGMT%2B8;4.5 现象管理员添加新书后用户端搜索不到重启 Tomcat 才生效复现步骤admin 添加一本《设计模式》立即用 student 账号搜索无结果重启 Tomcat 后才能搜到。原因BookDao.searchBooks()方法中使用了Statement而非PreparedStatement且未关闭连接导致连接池中缓存了旧的查询计划或 MySQL 查询缓存Query Cache未失效。解决彻底删除Statement全部改用PreparedStatement已在 3.3 节体现在 MySQL 中关闭查询缓存MySQL 8.0 已默认禁用5.7 需执行SET GLOBAL query_cache_size 0; SET GLOBAL query_cache_type 0;5. 让系统真正可用三处关键增强与一个必做验证技巧做到上面几步系统已能稳定运行但离“可交付毕设”还有差距。真正的价值不在“能跑”而在“能讲清楚每一处为什么这么改”。以下三点增强直击答辩高频追问。5.1 增强一为借阅记录增加“逾期天数”计算JSP 页面内嵌 Java 逻辑原始系统只显示borrow_date和return_date无法直观判断是否逾期。在user/borrowList.jsp中加入动态计算% page importjava.text.SimpleDateFormat % % page importjava.util.Date % % SimpleDateFormat sdf new SimpleDateFormat(yyyy-MM-dd); Date borrowDate rs.getDate(borrow_date); Date returnDate rs.getDate(return_date); int overdueDays 0; if (returnDate null) { // 未还书计算从借书日到今天的天数 long diff System.currentTimeMillis() - borrowDate.getTime(); overdueDays (int) (diff / (1000 * 60 * 60 * 24)); if (overdueDays 30) { // 逾期超30天标红 out.print(span stylecolor:red逾期 overdueDays 天/span); } else { out.print(已借 overdueDays 天); } } else { out.print(已归还); } %技巧说明System.currentTimeMillis() - borrowDate.getTime()是最轻量的日期差计算避免引入Calendar或LocalDate增加复杂度out.print()直接输出 HTML 片段比% %更灵活可嵌套 if 判断标红逻辑让答辩时能自然引出“如何用 JSP 动态控制样式”的知识点。5.2 增强二用 JSTL 替代 scriptlet 实现权限菜单教学价值拉满原始index.jsp中常写% if(admin.equals(session.getAttribute(role))){ % a href/library/admin/manageUser.jsp用户管理/a % } %改为 JSTL需确保WEB-INF/lib/有jstl.jar和standard.jar% taglib prefixc urihttp://java.sun.com/jsp/jstl/core % c:if test${sessionScope.role admin} a href/library/admin/manageUser.jsp用户管理/a /c:if c:if test${sessionScope.role user} a href/library/user/borrowList.jsp我的借阅/a /c:if优势sessionScope.role比session.getAttribute(role)更简洁且 EL 表达式天然防 NPEc:if是标准标签体现对 JSP 规范的理解答辩时可对比 scriptlet 的劣势难维护、混杂逻辑与视图无需修改 Java 代码纯前端增强风险极低。5.3 增强三添加数据库连接池DBCP提升并发稳定性原始JdbcUtil.java每次都DriverManager.getConnection()高并发下会耗尽连接。引入 DBCP下载commons-dbcp-1.4.jar和commons-pool-1.6.jar放入WEB-INF/lib/在WebContent/WEB-INF/context.xml中配置?xml version1.0 encodingUTF-8? Context Resource namejdbc/LibraryDB authContainer typejavax.sql.DataSource maxTotal20 maxIdle10 minIdle5 initialSize5 maxWaitMillis10000 usernameroot password123456 driverClassNamecom.mysql.cj.jdbc.Driver urljdbc:mysql://localhost:3306/library?useUnicodetrueamp;characterEncodingUTF-8amp;serverTimezoneGMT%2B8/ /Context修改JdbcUtil.javapublic static DataSource dataSource; static { try { Context initCtx new InitialContext(); Context envCtx (Context) initCtx.lookup(java:comp/env); dataSource (DataSource) envCtx.lookup(jdbc/LibraryDB); } catch (Exception e) { e.printStackTrace(); } } public static Connection getConnection() throws SQLException { return dataSource.getConnection(); // 从此获取连接池中的连接 }参数说明maxTotal20最大连接数避免 MySQLmax_connections超限maxWaitMillis10000获取连接超时 10 秒防止线程无限等待initialSize5启动时预建 5 个连接减少首次请求延迟。5.4 必做验证技巧用 Postman 模拟并发借书验证库存不超扣答辩老师最爱问“你怎么证明你的事务真的起了作用”光说“我加了FOR UPDATE”不够要拿出证据。用 Postman 做压力测试创建两个请求请求 APOST http://localhost:8080/library/servlet/BorrowBookServletBodyisbn9787508652345请求 B同上相同 ISBN。在 Postman 中用Runner功能设置 2 个迭代、并发数 2延迟 0ms观察结果✅ 成功一个请求返回“借书成功”另一个返回“库存不足”❌ 失败两个都成功库存变 -1。我的习惯每次改完事务逻辑必跑这组 Postman 测试截图存档。答辩时打开 Postman现场演示“并发借同一本书第二个人被拦住”比讲一百遍SELECT FOR UPDATE都管用。这不仅是技术验证更是工程思维的体现——你不是写完代码就结束而是定义验收标准、用工具证明它成立。希望帮到你。本文还有配套的精品资源点击获取
返回列表