ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

基于Web的图书管理系统:期末大作业从技术选型到部署避坑全攻略

基于Web的图书管理系统:期末大作业从技术选型到部署避坑全攻略 简介基于Web的图书管理系统期末大作业完整方案包含前端页面、后端业务逻辑与数据库设计覆盖用户登录、图书管理、借阅管理等典型功能适合计算机专业学生完成课程设计或期末项目参考。压缩包共91个文件大小8.05MB文件类型以Java源码和JSP页面为主另有CSS/JS前端资源、SQL数据库脚本、配置文件及项目说明文档目录结构清晰包含src源码目录与WebRoot静态资源目录便于导入IDE运行调试。当前已有1316人学习下载资源附带用户表、图书表、借阅表等SQL脚本可快速还原数据库环境配合项目说明文档梳理的模块划分与部署步骤能帮助读者理解系统整体架构并快速上手二次开发。整体设计完整模块划分合理既可用于期末作业提交与答辩展示也可作为功能扩展和实战练手的基础版本。1. 基于Web的图书管理系统为什么期末大作业都选它每到期末Web开发类课程的最后一个关卡总有一批学生对着同一个题目挠头基于Web的图书管理系统。它看起来不起眼却是历届作业里出现频率最高的一个几乎可以称得上是 Web 期末大作业的“守门员”。原因很直白一个图书管理系统恰好把 Web 开发从数据库设计到前后端联调的所有环节都覆盖了——图书的新增、查询、删除读者的登记与状态管理借书和还书两条业务线规模控制在两三周能完成复杂度又足够撑起答辩。源码加数据库一起交付老师能在本地直接跑起来看效果你也能拿到一份能讲清楚每一行代码的完整工程。2. 技术选型与项目架构期末Web图书管理系统用什么做最稳做基于Web的图书管理系统第一件事不是找源码是定技术路线。技术路线决定你后面所有的工作量也决定老师打开项目时的第一印象。2.1 图书管理系统的主流Web技术栈对比JSP、PHP还是Spring Boot期末大作业里最常见的三种路线JSP Servlet MySQLPHP MySQL以及 Spring Boot Thymeleaf MySQL。客观说没有绝对的好坏只有“和你的课程匹配度”的差别。技术栈上手成本环境复杂度老师偏好适合场景JSP Servlet中需要 Tomcat配置较繁琐教材用 Java Web 时首选计算机专业 Java 方向的经典作业PHP MySQL低装 phpStudy 或 XAMPP 即可能跑即可验收偏展示非科班、快速交差Spring Boot Thymeleaf高Maven 依赖常出问题学过框架的课程更认可要求分层规范、想写进简历的Python Flask/Django中需配虚拟环境选修课的常见选择Python 课设我见过太多学生拿着一个包装精美的 Spring Boot 项目去答辩结果老师问一句“依赖怎么下的”就卡住了。期末大作业的选型原则应该是你熟悉的优先而不是看起来高级的优先。如果你的课程就是 Java Web老老实实用 JSP Servlet这既是课程要求也是最好解释的方案。往下讲我都以 JSP Servlet MySQL 这条最经典的路线为例因为这套组合的源码和数据库脚本最容易在本地完整跑起来。2.2 数据库设计图书、读者、借阅三张核心表的字段与关系基于Web的图书管理系统本质上是一个“数据库增删改查”的壳数据库设计决定了系统能撑起多少业务逻辑。最少需要三张表图书表 book、读者表 reader、借阅表 borrow。借阅表是关联表它同时引用图书主键和读者主键形成多对多关系。-- 创建图书管理系统的数据库注意字符集选 utf8mb4避免中文乱码 CREATE DATABASE IF NOT EXISTS library_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE library_db; -- 图书表存书籍基础信息与库存 CREATE TABLE book ( id INT PRIMARY KEY AUTO_INCREMENT COMMENT 图书ID自增主键, book_name VARCHAR(100) NOT NULL COMMENT 书名不能为空, author VARCHAR(50) DEFAULT NULL COMMENT 作者, publisher VARCHAR(50) DEFAULT NULL COMMENT 出版社, isbn VARCHAR(20) DEFAULT NULL COMMENT ISBN编号, total_count INT DEFAULT 1 COMMENT 馆藏总量, remaining_count INT DEFAULT 1 COMMENT 当前可借数量每次借出后减一, publish_date DATE DEFAULT NULL COMMENT 出版日期 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 读者表读者的账号信息与状态 CREATE TABLE reader ( id INT PRIMARY KEY AUTO_INCREMENT COMMENT 读者ID, reader_name VARCHAR(50) NOT NULL COMMENT 读者姓名, phone VARCHAR(20) DEFAULT NULL COMMENT 联系电话, username VARCHAR(50) NOT NULL UNIQUE COMMENT 登录用户名唯一约束, password VARCHAR(50) NOT NULL COMMENT 登录密码期末作业可明文真实项目必须加密, status TINYINT DEFAULT 1 COMMENT 状态1正常 0禁用 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 借阅表记录每一笔借书、还书操作 CREATE TABLE borrow ( id INT PRIMARY KEY AUTO_INCREMENT COMMENT 借阅记录ID, book_id INT NOT NULL COMMENT 外键关联book表的id, reader_id INT NOT NULL COMMENT 外键关联reader表的id, borrow_date DATE NOT NULL COMMENT 借出日期, due_date DATE NOT NULL COMMENT 应还日期通常借出日期加30天, return_date DATE DEFAULT NULL COMMENT 实际归还日期未归还时为NULL, status TINYINT DEFAULT 0 COMMENT 状态0借出中 1已归还 2已逾期, KEY idx_book_id (book_id), KEY idx_reader_id (reader_id), CONSTRAINT fk_borrow_book FOREIGN KEY (book_id) REFERENCES book(id), CONSTRAINT fk_borrow_reader FOREIGN KEY (reader_id) REFERENCES reader(id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;这套表结构我做了两个容易被忽略的设计一个是图书表里的remaining_count一个是借阅表里的due_date。remaining_count记录“还能不能借”每次借书成功都要减一还书加一这是图书管理系统的核心业务状态。due_date是应还日期有了它“逾期”这个功能才能算出来而不是靠手写死。2.3 源码目录结构Web项目的分层与文件清单拿到一份图书管理系统的 Web 工程先别急着运行把目录结构读明白比什么都重要。一个标准的 JSP Servlet 项目是 Maven 结构或传统 Dynamic Web Project 结构前者有src/main/java后者直接是src。无论哪种核心分层都一致library-system/ ├── src/ │ ├── com/example/entity/ // 实体类Book.java, Reader.java, Borrow.java │ ├── com/example/dao/ // 数据库操作层BookDao.java, ReaderDao.java │ ├── com/example/servlet/ // 控制器BookServlet.java, BorrowServlet.java │ └── com/example/util/ // 工具类DBUtil.java 数据库连接 ├── webapp/ │ ├── admin/ // 后台管理页面book_list.jsp, book_add.jsp │ ├── reader/ // 读者端页面index.jsp, borrow.jsp │ ├── WEB-INF/ │ │ ├── web.xml // 部署描述符配置欢迎页和Servlet映射 │ │ └── lib/ // 数据库驱动mysql-connector-java.jar │ ├── login.jsp │ └── index.jsp └── database/ └── library.sql // 数据库脚本含建库建表和初始数据这里最关键的认知是JSP 页面放在 webapp 下可以直接被访问而 WEB-INF 下的东西浏览器永远访问不到。很多学生在期末翻车就是因为把 JSP 放到了 WEB-INF 里面然后访问页面时报 404还以为是源码有问题。另外数据库驱动 jar 包必须放在WEB-INF/lib下Tomcat 启动后才会加载它这是新手最容易漏的一步。理解了目录结构后面的部署排查才能有条理地走。3. 把源码跑起来的完整过程环境准备与最小启动命令“源码下载下来了数据库文件也给了但我跑不起来。”这是我听到最多的一句话。基于Web的图书管理系统跑不起来九成是环境问题和路径问题不是代码问题。这一章按顺序做每一步都有对应的检查点。3.1 环境准备JDK、MySQL、Tomcat版本怎么配图书管理系统是典型的 Java Web 项目环境就三个组件JDK 8、MySQL 5.7 或 8.0、Tomcat 8.5 或 9。版本不需要追求最新反而要避开太新的组合比如 JDK 17 配老 Tomcat 会出现模块访问报错。我一般建议JDK 8 Tomcat 8.5 MySQL 5.7这组合最稳网上能找到的排错案例也最多。在命令行验证环境是否就绪# 检查 JDK 版本能看到 1.8 即可 java -version # 检查 MySQL 服务是否启动Linux/Mac 下用这个 mysqladmin -u root -p status # Windows 下确认 8080 端口没被占用 netstat -ano | findstr :8080三个命令分别对应 Java 运行环境、数据库状态、Web 端口三个关键检查点。findstr :8080如果没有任何输出说明 8080 空闲Tomcat 能正常启动如果输出了 PID说明端口被占先处理端口问题再继续。3.2 初始化数据库用命令行导入数据库脚本数据库脚本通常是一个.sql文件里面已经写好了建库、建表和初始数据。拿到它之后直接在 MySQL 命令行里执行即可注意 Windows 下文件路径不要含中文否则 MySQL 可能读不到。# 进入 MySQL输入密码后进入命令行 mysql -u root -p # 在 MySQL 命令行中执行脚本文件 # source 后面是 SQL 文件的绝对路径 source D:/work/library_db.sql; # 查看数据库是否创建成功 SHOW DATABASES; # 进入图书管理系统数据库 USE library_db; # 查看三张核心表是否都在 SHOW TABLES;source是 MySQL 自带的命令比图形化工具更可靠。执行后如果看到Query OK就说明建表成功。有些数据库脚本会带DROP TABLE IF EXISTS重复执行不会报错这是好事说明作者考虑到了二次导入的场景。导入后建议顺手执行一次SELECT * FROM book;看看初始数据里有没有中文乱码。如果显示正常中文说明数据库字符集没问题后面乱码排查就少一个环节。3.3 修改数据库连接配置用户名、密码、时区参数源码里的数据库连接配置几乎不可能和你本机的 MySQL 账号完全一致所以这一步是必改项。连接配置通常集中在一个DBUtil.java或db.properties文件里。如果是DBUtil.java你会看到类似下面的代码// 数据库连接工具类整个Web工程唯一的连接入口 public class DBUtil { // 数据库驱动类名MySQL 5.7 和 8.0 通用 private static final String DRIVER com.mysql.cj.jdbc.Driver; // 连接地址localhost 是本机3306 是 MySQL 默认端口 // 库名是 library_db如果脚本里建的是别的库名这里要改 private static final String URL jdbc:mysql://localhost:3306/library_db?useSSLfalseserverTimezoneAsia/ShanghaicharacterEncodingutf8; // 本机 MySQL 的用户名通常是 root private static final String USER root; // 改成你自己 MySQL 的密码别用原作者的 private static final String PASSWORD 123456; public static Connection getConnection() throws SQLException { try { // 注册驱动只执行一次 Class.forName(DRIVER); } catch (ClassNotFoundException e) { throw new SQLException(MySQL驱动加载失败请检查 WEB-INF/lib 下的 jar 包); } // 每次调用返回一个新的数据库连接 return DriverManager.getConnection(URL, USER, PASSWORD); } }三个参数按你的实际环境改USER改成你的 MySQL 用户名PASSWORD改成你的真实密码URL里的library_db改成你导入的数据库名。serverTimezoneAsia/Shanghai这个参数不要删MySQL 8.0 默认时区是 UTC不指定的话Java 连接时会直接抛时区异常这是新版 MySQL 的著名坑。3.4 部署到 Tomcat 并启动 Web 服务部署这一步新手最容易在 IDE 里绕晕。最常见的做法是把 Web 工程打包成 WAR 文件或者直接把整个 webapp 目录扔到 Tomcat 的 webapps 下。如果源码给的是完整工程用 IDEA 或 Eclipse 导入后配置好 Tomcat 运行环境直接启动即可。# 进入 Tomcat 的 bin 目录 cd D:/apache-tomcat-8.5.100/bin # 启动 TomcatWindows 下 startup.bat # 启动 TomcatLinux/Mac 下 ./startup.sh # 启动后检查日志看到 Server startup 才是成功 tail -f D:/apache-tomcat-8.5.100/logs/catalina.out启动成功的标志不是浏览器能打开页面而是日志里出现Server startup in [xxxx] milliseconds。很多人 Tomcat 启动了页面白屏就是因为项目部署名不对。默认localhost:8080访问的是 ROOT 项目你的图书管理系统如果部署名是library-system访问路径应该是http://localhost:8080/library-system/login.jsp。4. 前端页面与后端接口怎么对上源码结构逐层拆解跑起来之后接下来的问题是这份源码我能不能讲清楚期末答辩最怕的就是“代码不是自己写的一问三不知”。把前端到数据库这条链路走一遍你就能讲出逻辑。4.1 前端 JSP 页面表单提交与页面跳转图书管理系统的主页面通常是登录页 图书列表页 借阅页。JSP 页面本质上是一个嵌了 Java 代码的 HTML但期末作业里最常见的写法是用 JSTL 和 EL 表达式来渲染数据这比在页面里写一堆% %看起来规范得多。!-- 图书新增页面的核心表单提交到 /book/add 这个路径 -- % page contentTypetext/html;charsetUTF-8 languagejava % % taglib prefixc urihttp://java.sun.com/jsp/jstl/core % html head title新增图书/title /head body h2新增图书/h2 !-- 表单的 action 决定了提交给哪个 Servlet -- form action${pageContext.request.contextPath}/book/add methodpost div label书名/label !-- name 属性是后端取参数的依据改名后后端也要同步改 -- input typetext namebookName required /div div label作者/label input typetext nameauthor /div div label出版社/label input typetext namepublisher /div div label馆藏总量/label !-- 数字输入框后端会做一次 parseInt -- input typenumber nametotalCount min1 value1 /div button typesubmit保存/button /form /body /html${pageContext.request.contextPath}是动态获取项目部署路径这样无论项目叫什么名字表单提交地址都不会因部署名变化而失效。表单里每个输入框的name属性就是后端request.getParameter()要用的键前后端不一致是联调最常见的低级错误。4.2 Servlet 控制器请求接收与业务分发Servlet 是前端和数据库之间的中间层它负责三件事接收请求参数、调用 DAO 层方法、决定跳转到哪个页面。// 新增图书的Servlet路径和前端表单的action一致 WebServlet(/book/add) public class BookAddServlet extends HttpServlet { Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 必须放在最前面处理POST请求的中文乱码 req.setCharacterEncoding(UTF-8); // 1. 从请求中取出表单参数 String bookName req.getParameter(bookName); String author req.getParameter(author); String publisher req.getParameter(publisher); String isbn req.getParameter(isbn); // 前端传来的字符串需要转换成整数 int totalCount Integer.parseInt(req.getParameter(totalCount)); // 2. 组装实体对象 Book book new Book(); book.setBookName(bookName); book.setAuthor(author); book.setPublisher(publisher); book.setIsbn(isbn); book.setTotalCount(totalCount); // 新书的可借数量等于馆藏总量 book.setRemainingCount(totalCount); // 3. 调用 DAO 层完成数据库插入 BookDao dao new BookDao(); int rows dao.insert(book); // 4. 根据结果做页面跳转 if (rows 0) { // 用重定向避免刷新时表单重复提交 resp.sendRedirect(req.getContextPath() /book/list); } else { req.setAttribute(msg, 新增图书失败请检查数据); req.getRequestDispatcher(/book/add.jsp).forward(req, resp); } } }这个 Servlet 的写法是标准的“接收—处理—跳转”三段式。注意最后的分支新增成功用sendRedirect跳转到列表页失败用forward回到表单页并携带错误提示。sendRedirect和forward的区别在于前者浏览器地址栏会变化且刷新不会重复提交后者地址栏不变适合带错误信息返回原页面。4.3 数据库操作层JDBC 增删改查与预编译语句DAO 层是访问数据库的唯一入口。图书管理系统里BookDao 至少要有 insert、deleteById、findAll、findByName 四个方法对应图书管理的增删查三个核心功能。// 图书数据库操作层封装所有sql语句 public class BookDao { // 按书名模糊查询图书支持列表页的搜索功能 public ListBook findByName(String keyword) { ListBook list new ArrayList(); // SQL语句用?占位符防止SQL注入 String sql SELECT * FROM book WHERE book_name LIKE ?; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { // %keyword% 实现模糊匹配注意%号是拼接进参数里的 ps.setString(1, % keyword %); try (ResultSet rs ps.executeQuery()) { while (rs.next()) { Book book new Book(); book.setId(rs.getInt(id)); book.setBookName(rs.getString(book_name)); book.setAuthor(rs.getString(author)); book.setPublisher(rs.getString(publisher)); book.setRemainingCount(rs.getInt(remaining_count)); list.add(book); } } } catch (SQLException e) { e.printStackTrace(); } return list; } // 新增图书返回受影响的行数1表示成功 public int insert(Book book) { String sql INSERT INTO book (book_name, author, publisher, isbn, total_count, remaining_count) VALUES (?,?,?,?,?,?); try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, book.getBookName()); ps.setString(2, book.getAuthor()); ps.setString(3, book.getPublisher()); ps.setString(4, book.getIsbn()); ps.setInt(5, book.getTotalCount()); ps.setInt(6, book.getRemainingCount()); return ps.executeUpdate(); } catch (SQLException e) { e.printStackTrace(); return 0; } } }这里必须强调用PreparedStatement而不是直接拼接 SQL。原因有两个一是?占位符会自动处理字符串中的单引号和特殊字符不会破坏 SQL 结构二是代码可读性强别人接手你的项目时能一眼看懂哪个参数对应哪个字段。另外注意try-with-resources写法连接、预编译语句、结果集三层的关闭顺序由 Java 自动处理比手动finally里关闭干净得多期末答辩时这个细节是加分项。4.4 借阅与还书的业务逻辑事务和数量校验借书和还书是图书管理系统里最复杂的两个动作因为它同时涉及 borrow 表和 book 表两张表的变动。借书成功 在 borrow 插入一条记录 book 的 remaining_count 减一两个操作必须同时成功否则数据就不一致。这里就引出了事务的概念。// 借书业务包含插入借阅记录和扣减库存两个步骤 public boolean borrowBook(int bookId, int readerId) { // 借书日期和应还日期应还日 借出日 30天 Date borrowDate new Date(); Calendar cal Calendar.getInstance(); cal.setTime(borrowDate); cal.add(Calendar.DAY_OF_YEAR, 30); Date dueDate cal.getTime(); String insertSql INSERT INTO borrow (book_id, reader_id, borrow_date, due_date, status) VALUES (?,?,?,?,0); // 关键WHERE 条件里带 remaining_count 0防止超借 String updateSql UPDATE book SET remaining_count remaining_count - 1 WHERE id ? AND remaining_count 0; Connection conn null; try { conn DBUtil.getConnection(); // 关闭自动提交两步操作变成同一个事务 conn.setAutoCommit(false); // 第一步插入借阅记录 try (PreparedStatement ps1 conn.prepareStatement(insertSql)) { ps1.setInt(1, bookId); ps1.setInt(2, readerId); ps1.setDate(3, new java.sql.Date(borrowDate.getTime())); ps1.setDate(4, new java.sql.Date(dueDate.getTime())); ps1.executeUpdate(); } // 第二步扣减库存检查受影响行数是否为0 int rows; try (PreparedStatement ps2 conn.prepareStatement(updateSql)) { ps2.setInt(1, bookId); rows ps2.executeUpdate(); } // 减库存失败说明书已被借完回滚并返回失败 if (rows 0) { conn.rollback(); return false; } // 两步都成功才提交事务 conn.commit(); return true; } catch (SQLException e) { e.printStackTrace(); // 任何一步出错回滚整个事务 try { if (conn ! null) conn.rollback(); } catch (SQLException ex) { ex.printStackTrace(); } return false; } finally { // 恢复自动提交避免连接池里的连接状态被污染 if (conn ! null) { try { conn.setAutoCommit(true); conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } }这段代码有两个刻意设计conn.setAutoCommit(false)开启事务保证两步操作要么都成功要么都失败UPDATE语句里带remaining_count 0条件从数据库层面杜绝库存变负数。如果你发现你拿到的源码没有这两段逻辑那我建议你加上因为“库存变成负数”是图书管理系统答辩时最容易被老师抓的问题。5. 图书管理系统项目避坑与常见问题排查交作业前必看无论源码是自己写的还是拿来的期末作业的验收现场总会出点幺蛾子。下面是图书管理系统上线前必须排查的五类高频问题每一条都是血泪经验。5.1 数据库连接失败驱动、密码、时区三个坑现象启动项目后访问查询图书列表的页面直接报Communications link failure或者Access denied for user。原因这三个报错对应的原因完全不同。Communications link failure说明网络层面就连不上 MySQL常见原因是 MySQL 服务没启动或者连接 URL 里的端口号写错Access denied则是用户名或密码不对还有一个高频隐藏原因MySQL 8.0 的驱动类名从com.mysql.jdbc.Driver改成了com.mysql.cj.jdbc.Driver如果源码里写的还是老驱动名会直接报ClassNotFoundException。解决先系统服务里确认 MySQL 在运行再检查DBUtil.java里的用户名密码是否匹配最后看连接 URL 里有没有serverTimezoneAsia/Shanghai。这三项按顺序查两分钟内能定位绝大多数连接失败问题。5.2 中文乱码的三级排查现象页面上书名显示成???或者数据库里存的是中文页面上却是一堆乱码。原因图书管理系统的中文乱码逃不出三个位置数据库字符集、连接 URL 字符集、JSP/Servlet 请求响应字符集。数据库建库时没指定utf8mb4连接时没带characterEncodingutf8Servlet 里request.setCharacterEncoding(UTF-8)没写在最前面三处任一不对都会乱码。解决按“数据库 → 连接 → 代码”顺序排查。先看建表语句是不是DEFAULT CHARSETutf8mb4再看 JDBC URL 是否带characterEncodingutf8最后在所有 Servlet 的doPost第一行加req.setCharacterEncoding(UTF-8)。JSP 页面顶部记得写contentTypetext/html;charsetUTF-8。三个位置全对了中文乱码基本绝迹。5.3 Tomcat 启动失败端口被占用与部署路径现象Tomcat 启动不到两秒就闪退控制台报Address already in use: JVM_Bind :8080或者启动成功但访问所有页面都是 404。原因Address already in use是 8080 端口被别的程序占了常见的有其他 Tomcat 实例、IDEA 内置服务器、甚至某些软件的调试端口。404 则是部署名对不上项目明明叫library-system你却访问localhost:8080/。解决端口占用时Windows 下用netstat -ano | findstr :8080查到占用 PID再到任务管理器里结束对应的进程不想杀进程的话改 Tomcat 的conf/server.xml里端口号为 8081 也行。404 问题看一眼 Tomcat 的webapps目录下文件夹名按实际部署名拼接访问路径。5.4 库存变成负数和还书日期异常现象同一本书被连续借出remaining_count一路减到 -3或者还书后日期显示成 1970 年。原因库存负数是因为借书逻辑只做了“INSERT 借阅记录”和“UPDATE 库存减一”没判断库存是否已经为零。多个读者同时操作时甚至会出现两个人同时借走最后一本的情况。日期变 1970 是 Java 中Date和java.sql.Date转换出问题常见于直接把util.Date塞给setDate()方法或毫秒数丢失。解决按第 4 章的写法UPDATE语句里加AND remaining_count 0再用受影响行数判断是否扣减成功。日期转换用new java.sql.Date(utilDate.getTime())不要直接传对象我在实际排错时见过太多次这个低级但隐蔽的坑了。顺带说一句如果这些逻辑在你的源码里都没有这恰恰是你答辩时能主动补强的地方。5.5 表单重复提交与刷新后重复扣库存现象借书时手一抖点了两次按钮系统里多了两条一模一样的借阅记录库存也连着减了两次。原因前端没有防双击后端又没有做重定向跳转。用户点按钮后页面没跳转再点一次又发了一个 POST 请求或者操作成功后停留在原页面按 F5 刷新浏览器重新提交上一次的 POST 请求。解决前端在表单提交后把按钮设置为disabled只需要一行 JS后端在成功操作后统一用resp.sendRedirect()不要用forward()或直接返回视图。这就是经典的 PRGPost-Redirect-Get模式面试时能说出这个名词说明你真的理解了这个坑。6. 交作业前的最后冲刺演示录像与答辩讲解项目跑通只完成了一半期末作业的验收是“看演示 问原理”所以最后一步是把演示流程和讲解逻辑打磨好。6.1 功能演示脚本录屏时按这个顺序操作录屏演示时不要手握鼠标乱点。按一个严谨的业务流程走一遍比零散地展示十个页面更有说服力。推荐的演示顺序展示数据库在 MySQL 里执行SELECT * FROM book让老师看到三张表的数据这是“数据库设计”的实证。打开系统输入管理员账号登录展示登录跳转。新增一本图书填写表单提交后到列表页确认新书出现。模拟一次完整借阅选择一个读者借出刚新增的图书回到详情页看库存减一。执行还书确认库存加一借阅记录状态变为已归还。演示一个边界场景把图书库存借到 0再尝试借阅系统应提示“库存不足”。6.2 答辩讲解的切入点答辩被问得最多的三个问题是表结构为什么这么设计借阅逻辑怎么保证数据不错项目如果上线还缺什么对应准备三句话表结构用“三张表借阅表做关联冗余了 remaining_count 和 due_date 两个关键字段”借阅逻辑讲“事务 库存判断两步操作要么都成功要么都失败”上线缺什么讲“密码加密、分页查询、并发控制、日志记录”。关于版权问题多说一句如果是网上找的源码务必把 README 里的作者信息、版权声明删掉或注明出处把代码里的包名、类名改成自己的风格。老师看重的不是代码多高级而是你能不能把每行逻辑讲明白。把源码从头到尾读一遍标注出每段代码的作用你就能把别人的工程变成自己的作品。这套方法我带过很多学生走通希望帮到你。本文还有配套的精品资源点击获取
返回列表