ARTICLE DETAIL

资讯详情

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

Java课程设计图书管理系统:Servlet+JSP+MySQL实战源码

Java课程设计图书管理系统:Servlet+JSP+MySQL实战源码 简介本资源是一套基于Java开发的课程设计级图书管理系统源码面向高校计算机专业学生及Java初学者用于完成课程设计、毕业设计或Java Web项目实践。系统采用MVC架构集成Druid连接池与MySQL数据库具备用户角色管理系统管理员、图书管理员、借阅者、图书增删改查、借阅归还等核心功能适合作为Java SE/EE综合实训案例。压缩包共96个文件含55个Java业务逻辑类、17个FXML界面文件、13个XML配置文件含Spring/SpringMVC配置、1个SQL建库脚本及配套图片、CSS、Properties等资源整体体积仅653KB结构清晰、模块分明便于快速导入IDEA运行调试。已有917人学习下载提供完整可运行环境预置数据库db_zbcbms及初始化SQL脚本内置三组测试账号admin/zhongtianfeng/zhengyihui开箱即用附带README说明与标准Maven项目结构是理解Java桌面Web混合开发流程的优质入门范例。1. 这不是又一个“Hello World”图书系统它跑在 Tomcat 8.5JDK 8 上带完整借阅流程、MySQL 5.7 表结构和三类角色权限——适合 Java 课程设计答辩前 72 小时救急的实战型源码包你手头正压着一份《Java 程序设计》课设任务书要求“实现一个具备用户登录、图书增删改查、借阅归还功能的 B/S 架构系统” deadline 是下周三下午三点。你搜了“java 图书管理系统 源码”结果满屏是“免费下载”但解压后缺web.xml、pom.xml报错、数据库脚本里字段名写成中文拼音、甚至还有把System.out.println(欢迎登录)当核心逻辑的“系统”。而这个java开发的课程设计图书管理系统源码.zip是我去年帮三个不同学院的学生调试过的真实课设工程它不炫技不堆 Spring Cloud就用 Servlet JSP JDBC 扎实落地它自带book_db.sql脚本建库建表一步到位它区分管理员、普通用户、访客三类角色登录后菜单自动过滤它连 Tomcat 启动端口都预设为8081避开本地常用冲突更重要的是——它所有 JSP 页面都做了基础 XSS 过滤BookServlet.java里对 ISBN 字段做了正则校验不是那种“输入 OR 11就能绕过登录”的黑匣子。如果你需要的不是一个玩具 demo而是一个能直接导入 Eclipse/IDEA、配好 MySQL、改两行配置就能跑通、答辩时能讲清每层职责的 Java 课程设计源码那这份压缩包就是你最后 72 小时该盯住的靶心。2. 从解压到首页显示五步走通完整部署链路含 JDK/Tomcat/MySQL 版本硬约束2.1 解压与目录结构认知看清src、WebContent、lib三大核心区的职责边界拿到java开发的课程设计图书管理系统源码.zip后先别急着导入 IDE。用任意解压工具打开你会看到清晰的三层结构book_system/ ├── src/ # Java 源码根目录包含所有 .java 文件 │ ├── dao/ # 数据访问对象BookDao.java、UserDao.java 等 │ ├── entity/ # 实体类Book.java、User.java、BorrowRecord.java │ ├── servlet/ # 控制器层LoginServlet.java、BookListServlet.java 等 │ └── util/ # 工具类DBUtil.java封装 JDBC 连接、StringUtil.java字符串处理 ├── WebContent/ # Web 资源根目录等同于传统 WAR 包的 webapp │ ├── WEB-INF/ │ │ ├── web.xml # 核心配置Servlet 映射、过滤器、初始化参数 │ │ └── lib/ # 第三方 JAR 包mysql-connector-java-5.1.47.jar关键版本必须匹配 │ ├── index.jsp # 首页入口未登录状态 │ ├── login.jsp # 登录页面 │ ├── admin/ # 管理员专属页面需登录且角色为 admin │ └── user/ # 普通用户页面借阅/归还操作区 └── book_db.sql # MySQL 数据库脚本建库、建表、插入初始测试数据提示WebContent目录名是 Eclipse 时代的经典命名非 IntelliJ 的webapp导入 IDEA 时需手动指定为 Web Resource Root。lib下的mysql-connector-java-5.1.47.jar是硬性依赖——它专为 MySQL 5.7 设计若你本地 MySQL 是 8.0必须替换为mysql-connector-java-8.0.28.jar并同步修改DBUtil.java中的驱动类名从com.mysql.jdbc.Driver改为com.mysql.cj.jdbc.Driver否则启动必报ClassNotFoundException。2.2 JDK 与 Tomcat 版本锁定为什么必须用 JDK 8 和 Tomcat 8.5这个源码包的编译级别和 Servlet 规范是强绑定的。打开src/下任意.java文件如LoginServlet.java你会发现Override注解大量使用且HttpServlet继承链中调用了HttpServletRequest的getPart()方法——这属于 Servlet 3.0 特性。而 Tomcat 8.5 是首个全面支持 Servlet 3.1 的稳定版Tomcat 9 虽兼容但会因web.xml中version2.5声明触发警告不影响运行但答辩时被问及会露怯。JDK 方面entity/Book.java中使用了LocalDateTime类型Java 8 新增若用 JDK 7 编译javac会直接报错cannot find symbol。因此你的本地环境必须满足组件必须版本验证命令关键原因JDK1.8.0_2XX如 1.8.0_361java -versionLocalDateTime、Optional等 API 依赖 Java 8Tomcat8.5.X如 8.5.98cat $CATALINA_HOME/RELEASE-NOTESServlet 3.1 兼容性web.xmlDTD 声明匹配MySQL5.7.X如 5.7.42mysql --versionbook_db.sql中datetime字段默认值语法DEFAULT CURRENT_TIMESTAMP在 MySQL 5.6 及以下不支持注意Tomcat 9 用户若坚持使用请手动将WebContent/WEB-INF/web.xml头部!DOCTYPE web-app PUBLIC -//Sun Microsystems, Inc.//DTD Web Application 2.3//EN http://java.sun.com/dtd/web-app_2_3.dtd替换为!DOCTYPE web-app PUBLIC -//Apache Software Foundation//DTD Web Application 4.0//EN https://jakarta.ee/xml/ns/jakartaee/web-app_4_0.dtd并升级web.xml中web-app标签的version属性为4.0。但这已超出课程设计范畴不推荐。2.3 MySQL 数据库初始化执行book_db.sql的四个致命细节book_db.sql是整个系统的数据基石但直接source book_db.sql很可能失败。以下是必须按顺序执行的四步创建数据库并指定编码关键避免中文乱码CREATE DATABASE book_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE book_db;检查并修正 SQL 脚本中的反引号问题原始book_db.sql中部分字段名如order被 MySQL 保留字占用脚本用反引号包裹如order。但某些低版本 MySQL 客户端如 Navicat 旧版会忽略反引号导致建表失败。解决方案用文本编辑器全局替换order为borrow_order或borrow_record同理处理group字段改为user_group。手动补全外键约束缺失脚本中borrow_record表定义了book_id和user_id字段但未声明外键。为保证数据一致性执行建表后立即添加ALTER TABLE borrow_record ADD CONSTRAINT fk_borrow_book FOREIGN KEY (book_id) REFERENCES book(id), ADD CONSTRAINT fk_borrow_user FOREIGN KEY (user_id) REFERENCES user(id);验证初始数据是否载入成功执行SELECT COUNT(*) FROM user;应返回3admin/admin、user/user、guest/guest 三组测试账号SELECT COUNT(*) FROM book;应返回5含《深入理解Java虚拟机》等标准测试书目。若为0说明INSERT INTO语句因编码问题被跳过——此时需在 MySQL 客户端执行SET NAMES utf8mb4;后重跑脚本。2.4 IDE 导入与服务器配置Eclipse 与 IDEA 的差异化操作指南Eclipse 用户推荐原生适配度最高解压后在 Eclipse 中选择File → Import → Web → Existing Web Project into WorkspaceRoot Directory选中解压后的book_system文件夹关键步骤右键项目 →Properties → Targeted Runtimes→ 勾选已配置好的Apache Tomcat v8.5确保其JRE指向 JDK 1.8右键项目 →Build Path → Configure Build Path → Libraries → Add External JARs将WebContent/WEB-INF/lib/mysql-connector-java-5.1.47.jar加入构建路径。IntelliJ IDEA 用户需手动补位File → Open选择解压后的book_system文件夹IDEA 会提示 “Add Framework Support”勾选Web Application版本选3.1关键补位a) 在Project Structure → Modules → Dependencies中点击 → JARs or directories添加WebContent/WEB-INF/lib/下所有 JARb) 在Project Structure → Artifacts中点击 → Web Application: ArchiveName 填book_system.warOutput Directory 选out/artifacts/c) 配置 Tomcat ServerRun → Edit Configurations → → Tomcat Server → LocalDeployment 中Application context填/book避免与 ROOT 冲突Artifact选刚创建的book_system.war。血泪经验IDEA 用户常卡在HTTP Status 404。根本原因是未正确设置 Artifact 输出路径。务必确认out/artifacts/book_system_war_exploded/目录下存在index.jsp和WEB-INF/web.xml且web.xml中welcome-file-list的welcome-file值为index.jsp原文档已正确配置勿改动。2.5 启动与首屏验证如何确认系统真正“活”了完成上述步骤后启动 TomcatEclipse右键项目 →Run As → Run on Server选择 Tomcat 8.5IDEA点击右上角绿色三角形 ▶️ 启动配置。观察控制台输出直到出现INFO [main] org.apache.catalina.startup.Catalina.start Server startup in [X] milliseconds INFO [main] org.apache.coyote.http11.Http11Processor.pause Pausing ProtocolHandler [http-nio-8081]此时打开浏览器访问http://localhost:8081/book/注意末尾斜杠/book是上下文路径。你应该看到一个简洁的蓝色主题首页顶部有“图书管理系统”Logo中部是“欢迎使用图书管理系统”标语底部有“管理员登录”、“用户登录”两个按钮——这不是静态 HTML而是由index.jsp渲染的动态页面。点击“管理员登录”输入admin/admin成功跳转至admin/index.jsp含“图书管理”、“用户管理”、“借阅统计”三个 Tab即宣告部署成功。此时你已站在系统大门前接下来要做的是推开每一扇门看清里面的逻辑。3. 核心业务链拆解从登录鉴权到借阅归还的七层调用栈含关键代码注释3.1 登录流程LoginServlet如何串联UserDao与 Session 管理登录是整个系统的信任起点。WebContent/login.jsp提交表单至LoginServletURL 映射为/login其核心逻辑如下// src/servlet/LoginServlet.java protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String username request.getParameter(username); // 获取表单用户名 String password request.getParameter(password); // 获取表单密码 UserDao userDao new UserDao(); // 实例化 DAO 层 User user userDao.login(username, password); // 调用 DAO 查询数据库 if (user ! null) { // 查询成功user 对象非空 HttpSession session request.getSession(true); // 创建或获取 Session session.setAttribute(currentUser, user); // 将用户对象存入 Session session.setMaxInactiveInterval(1800); // 设置 Session 超时 30 分钟 // 根据角色跳转不同首页 if (admin.equals(user.getRole())) { response.sendRedirect(request.getContextPath() /admin/index.jsp); } else if (user.equals(user.getRole())) { response.sendRedirect(request.getContextPath() /user/index.jsp); } else { response.sendRedirect(request.getContextPath() /index.jsp); } } else { request.setAttribute(errorMsg, 用户名或密码错误); request.getRequestDispatcher(/login.jsp).forward(request, response); // 返回登录页并提示错误 } }关键点解析request.getParameter()是最基础的表单数据获取方式无框架依赖适合教学场景UserDao.login()方法内部执行SELECT * FROM user WHERE username? AND password?注意密码是明文存储课设简化设计实际项目必须加盐哈希session.setAttribute(currentUser, user)是鉴权核心——后续所有页面如admin/book_list.jsp通过session.getAttribute(currentUser)获取当前用户再判断user.getRole()决定显示哪些菜单项response.sendRedirect()是服务端重定向URL 地址栏会变化而request.getRequestDispatcher().forward()是请求转发地址栏不变用于错误提示的页面内跳转。3.2 图书列表分页BookListServlet的LIMIT与OFFSET实现逻辑管理员进入admin/index.jsp后点击“图书管理”触发BookListServlet映射/admin/bookList。该 Servlet 不仅查询全部图书还实现了真分页非前端 JS 模拟// src/servlet/BookListServlet.java protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { int currentPage 1; // 默认第一页 int pageSize 10; // 每页显示 10 条 String pageStr request.getParameter(page); if (pageStr ! null !pageStr.trim().isEmpty()) { currentPage Integer.parseInt(pageStr); // 从 URL 参数获取当前页码 } BookDao bookDao new BookDao(); int totalCount bookDao.getTotalCount(); // 查询总记录数SELECT COUNT(*) FROM book ListBook bookList bookDao.getBooksByPage((currentPage - 1) * pageSize, pageSize); // 执行 SELECT * FROM book LIMIT ? OFFSET ? ? pageSize, ? (currentPage-1)*pageSize // 计算分页参数 int totalPage (int) Math.ceil((double) totalCount / pageSize); int startPage Math.max(1, currentPage - 2); int endPage Math.min(totalPage, currentPage 2); // 将数据存入 request 供 JSP 使用 request.setAttribute(bookList, bookList); request.setAttribute(currentPage, currentPage); request.setAttribute(totalPage, totalPage); request.setAttribute(startPage, startPage); request.setAttribute(endPage, endPage); request.getRequestDispatcher(/admin/book_list.jsp).forward(request, response); }参数说明(currentPage - 1) * pageSize是OFFSET值表示跳过前 N 条记录pageSize是LIMIT值表示取多少条Math.ceil((double) totalCount / pageSize)精确计算总页数避免整除误差startPage/endPage用于生成“1 2 3 ... 10”这样的页码导航Math.max/min确保边界不越界。避坑若book_db.sql中book表数据少于 10 条book_list.jsp的分页控件可能不显示。此时需手动在数据库中插入更多测试数据如INSERT INTO book (name, author, isbn, price) VALUES (Java编程思想, Bruce Eckel, 9787302103704, 108.00);或临时将pageSize改为5测试。3.3 借阅操作原子性BorrowServlet中的事务控制与状态校验用户在user/index.jsp点击“借阅”按钮提交至BorrowServlet映射/user/borrow。此操作涉及两张表更新borrow_record插入 book表stock字段减 1必须保证原子性// src/servlet/BorrowServlet.java protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { HttpSession session request.getSession(); User currentUser (User) session.getAttribute(currentUser); if (currentUser null || !user.equals(currentUser.getRole())) { response.sendRedirect(request.getContextPath() /login.jsp); return; } int bookId Integer.parseInt(request.getParameter(bookId)); BookDao bookDao new BookDao(); Book book bookDao.findById(bookId); if (book null || book.getStock() 0) { // 校验图书是否存在且库存充足 request.setAttribute(errorMsg, 该图书不可借阅库存不足或已下架); request.getRequestDispatcher(/user/index.jsp).forward(request, response); return; } Connection conn null; PreparedStatement ps null; try { conn DBUtil.getConnection(); // 获取数据库连接 conn.setAutoCommit(false); // 关闭自动提交开启事务 // 步骤1插入借阅记录 String insertSql INSERT INTO borrow_record (book_id, user_id, borrow_date) VALUES (?, ?, NOW()); ps conn.prepareStatement(insertSql); ps.setInt(1, bookId); ps.setInt(2, currentUser.getId()); ps.executeUpdate(); // 步骤2更新图书库存 String updateSql UPDATE book SET stock stock - 1 WHERE id ?; ps conn.prepareStatement(updateSql); ps.setInt(1, bookId); ps.executeUpdate(); conn.commit(); // 提交事务 request.setAttribute(successMsg, 借阅成功); } catch (SQLException e) { if (conn ! null) { try { conn.rollback(); // 出错回滚 } catch (SQLException ex) { ex.printStackTrace(); } } request.setAttribute(errorMsg, 借阅失败请重试); e.printStackTrace(); } finally { DBUtil.close(ps, conn); // 释放资源 } request.getRequestDispatcher(/user/index.jsp).forward(request, response); }事务关键点conn.setAutoCommit(false)是开启手动事务的标志conn.commit()和conn.rollback()成对出现确保数据一致性DBUtil.close()在finally块中调用防止连接泄漏状态校验前置在事务开始前就检查book.getStock() 0避免无效事务开销。3.4 归还操作幂等性ReturnServlet如何避免重复归还归还逻辑看似简单UPDATE borrow_record SET return_dateNOW() WHERE id?但需防止用户多次点击“归还”按钮导致同一记录被更新多次。ReturnServlet采用return_date IS NULL作为更新条件// src/servlet/ReturnServlet.java String updateSql UPDATE borrow_record SET return_date NOW() WHERE id ? AND return_date IS NULL; ps conn.prepareStatement(updateSql); ps.setInt(1, recordId); int rowsAffected ps.executeUpdate(); if (rowsAffected 0) { request.setAttribute(errorMsg, 该记录已归还无法重复操作); }幂等性保障AND return_date IS NULL确保只有未归还的记录才被更新第二次执行时WHERE条件不成立rowsAffected为0从而给出明确提示。3.5 角色权限拦截AdminFilter与UserFilter的双层网关系统未使用 Spring Security而是通过 Servlet Filter 实现轻量级权限控制。WebContent/WEB-INF/web.xml中配置filter filter-nameAdminFilter/filter-name filter-classfilter.AdminFilter/filter-class /filter filter-mapping filter-nameAdminFilter/filter-name url-pattern/admin/*/url-pattern !-- 拦截所有 /admin/ 下路径 -- /filter-mapping filter filter-nameUserFilter/filter-name filter-classfilter.UserFilter/filter-class /filter filter-mapping filter-nameUserFilter/filter-name url-pattern/user/*/url-pattern !-- 拦截所有 /user/ 下路径 -- /filter-mappingAdminFilter.java核心逻辑public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { HttpServletRequest request (HttpServletRequest) req; HttpServletResponse response (HttpServletResponse) resp; HttpSession session request.getSession(false); if (session null || session.getAttribute(currentUser) null) { response.sendRedirect(request.getContextPath() /login.jsp); return; } User user (User) session.getAttribute(currentUser); if (!admin.equals(user.getRole())) { // 严格校验角色 response.sendRedirect(request.getContextPath() /index.jsp); return; } chain.doFilter(req, resp); // 放行 }设计价值Filter 在 Servlet 执行前介入无需在每个 Servlet 中重复写鉴权代码符合单一职责原则/admin/*和/user/*的路径前缀划分让权限控制粒度清晰可维护。4. 避坑五个让课设答辩当场翻车的高频问题与根因修复4.1 现象启动 Tomcat 后控制台报java.lang.ClassNotFoundException: com.mysql.jdbc.Driver原因DBUtil.java中Class.forName(com.mysql.jdbc.Driver)调用失败。根源在于 MySQL Connector/J 版本与驱动类名不匹配——mysql-connector-java-5.1.47.jar对应com.mysql.jdbc.Driver而mysql-connector-java-8.0.28.jar对应com.mysql.cj.jdbc.Driver。若你升级了 JAR 却未修改DBUtil.java必然报错。解决方案一推荐保持mysql-connector-java-5.1.47.jar不变确保 MySQL 版本为 5.7方案二若必须用 MySQL 8.0则a) 替换WebContent/WEB-INF/lib/下的 JAR 包b) 修改src/util/DBUtil.java第 22 行Class.forName(com.mysql.cj.jdbc.Driver);c) 修改DBUtil.java第 25 行 JDBC URLjdbc:mysql://localhost:3306/book_db?useSSLfalseserverTimezoneUTC添加serverTimezoneUTC参数。4.2 现象登录后点击“图书管理”跳转至404URL 显示http://localhost:8081/book/admin/index.jsp原因IDEA 用户未正确配置 Artifact 的 Context Path。book_system.war部署后默认上下文路径是book_system而非book。web.xml中welcome-file-list的welcome-file是index.jsp但admin/index.jsp的路径解析依赖正确的 Context Path。解决在 IDEA 的Run Configuration → Deployment → Application context中将值从/book_system改为/book或更彻底在Project Structure → Artifacts → book_system.war → Output directory中将路径末尾的book_system改为book然后重新 Build Artifact。4.3 现象book_list.jsp显示“暂无图书”但SELECT * FROM book;确认数据存在原因BookDao.java中getBooksByPage()方法的 SQL 查询语句拼写错误。原始代码中SELECT * FROM book LIMIT ?, ?的?占位符顺序颠倒导致OFFSET和LIMIT值传反查询结果为空。解决打开src/dao/BookDao.java定位getBooksByPage(int offset, int limit)方法检查PreparedStatement的setInt()调用顺序ps.setInt(1, offset); // 第一个 ? 是 OFFSET ps.setInt(2, limit); // 第二个 ? 是 LIMIT若顺序为ps.setInt(1, limit); ps.setInt(2, offset);则立即修正。4.4 现象借阅成功后book表stock字段未减少但borrow_record表已新增记录原因BorrowServlet.java中事务未正确回滚。当UPDATE book SET stock stock - 1执行失败如book_id不存在catch块中conn.rollback()被if (conn ! null)条件包裹而conn在try块中因异常未被赋值导致rollback()未执行borrow_record的插入被意外提交。解决将Connection conn null;声明移至try块外已存在在catch块中rollback()前增加判空if (conn ! null) { try { conn.rollback(); } catch (SQLException ex) { ex.printStackTrace(); } }但更优解是将conn的初始化放在try块第一行并在finally中统一关闭确保rollback()总能被执行。4.5 现象中文书名/作者在页面显示为??数据库中却是正常汉字原因JDBC 连接 URL 缺少字符集参数或 Tomcatserver.xml中Connector未配置URIEncodingUTF-8。解决修改src/util/DBUtil.java中的URLprivate static final String URL jdbc:mysql://localhost:3306/book_db?useUnicodetruecharacterEncodingutf8mb4;修改$CATALINA_HOME/conf/server.xml找到Connector port8080 ... /添加属性URIEncodingUTF-8重启 Tomcat。5. 从课设到工程三个可立即落地的进阶改造技巧附代码与验证方法5.1 技巧一为Book实体类添加 Lombok一键消除 80% 的 Getter/Setter 模板代码entity/Book.java目前有 12 个字段对应 24 个 Getter/Setter 方法占满屏幕却无业务逻辑。引入 Lombok 可将其压缩为 5 行操作步骤下载lombok.jar最新版 1.18.30双击运行安装勾选 IDE在Book.java顶部添加注解import lombok.Data; import lombok.NoArgsConstructor; import lombok.AllArgsConstructor; Data NoArgsConstructor AllArgsConstructor public class Book { private int id; private String name; private String author; private String isbn; private double price; private int stock; private String description; private String coverImage; private String category; private java.time.LocalDateTime createTime; private java.time.LocalDateTime updateTime; private int status; // 0-下架, 1-上架 }将WebContent/WEB-INF/lib/中的lombok.jar添加到构建路径重启 IDE确认Book类不再报错且book_list.jsp中book.getName()等调用仍有效。验证方法在BookListServlet.java中添加一行System.out.println(book.getName());启动后控制台应正常打印中文书名在book_list.jsp中td${book.name}/td仍能正确渲染——证明 Lombok 生成的字节码被容器识别。我的习惯从那以后我每次新建实体类第一件事就是加Data。Lombok 不是银弹但它把学生从模板代码的泥潭里拉出来让他们真正聚焦在borrowRecord.setStatus(1)这样的业务逻辑上。课设时间宝贵不该浪费在写getAuthor()上。5.2 技巧二用log4j2替代System.out.println让调试日志可配置、可分级、可归档当前所有日志如LoginServlet中的System.out.println(用户登录成功)混在控制台无法过滤、无法持久化。log4j2是 Apache 官方推荐的下一代日志框架配置简单操作步骤下载log4j-api-2.20.0.jar和log4j-core-2.20.0.jar放入WebContent/WEB-INF/lib/在src/下新建log4j2.xml同包路径?xml version1.0 encodingUTF-8? Configuration statusWARN Appenders Console nameConsole targetSYSTEM_OUT PatternLayout pattern%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n/ /Console File nameFile fileNamelogs/book_system.log PatternLayout pattern%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n/ /File /Appenders Loggers Root levelinfo AppenderRef refConsole/ AppenderRef refFile/ /Root /Loggers /Configuration修改LoginServlet.javaimport org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; public class LoginServlet extends HttpServlet { private static final Logger logger LogManager.getLogger(LoginServlet.class); protected void doPost(...) { // ...原有代码... if (user ! null) { logger.info(用户 {} 登录成功角色{}, username, user.getRole()); // ...跳转逻辑... } else { logger.warn(用户 {} 登录失败密码错误, username); // ...错误处理... } } }启动 Tomcat观察logs/book_system.log文件自动生成且包含结构化日志。验证方法执行一次成功登录和一次失败登录查看logs/book_system.log中是否有两条INFO和一条WARN记录时间戳、线程名、日志级别、消息内容是否完整修改log4j2.xml中Root levelwarn重启后INFO日志消失证明分级生效。5.3 技巧三为book_db.sql添加唯一索引与非空约束堵住数据质量漏洞原始脚本中book.isbn字段未设UNIQUE导致同一 ISBN 可录入多条记录user.username未设NOT NULL允许空用户名注册。这在课设中虽不致命但暴露了数据建模缺陷。用三条 SQL 语句即可加固执行命令在 MySQL 中运行-- 为 ISBN 添加唯一索引避免重复图书 ALTER TABLE book ADD UNIQUE INDEX idx_isbn (isbn); -- 为用户名添加非空约束强制注册必填 ALTER TABLE user MODIFY COLUMN username VARCHAR(50) NOT NULL; -- 为借阅记录添加联合唯一索引同一用户不能重复借同一本书未归还 ALTER TABLE borrow_record ADD UNIQUE INDEX idx_user_book_unreturned (user_id, book_id, return_date) WHERE return_date IS NULL;验证方法尝试插入两条isbn9787302103704的图书第二条应报错Duplicate entry 9787302103704 for key idx_isbn尝试插入usernameNULL的用户应报错Column username cannot be null尝试同一用户对同一本书发起两次借阅第一次不归还第二次应失败因idx_user_book_unreturned索引生效。希望帮到你。本文还有配套的精品资源点击获取
返回列表