JavaWeb在线购书管理系统期末大作业:JSP+Servlet+MySQL完整实现

发布时间:2026/9/28 11:54:35
JavaWeb在线购书管理系统期末大作业:JSP+Servlet+MySQL完整实现 简介面向计算机相关专业学生完成课程设计或期末大作业的现实需求这套基于JavaWeb的在线购书管理系统提供了完整可运行的源码和配套数据库脚本项目经导师指导并认可评审得分为98分可以说是高分项目参考范本。系统围绕在线购书业务展开实现图书分类浏览、热门图书展示、用户注册登录、购物车增减、订单生成与提交、后台数据管理等核心功能前端JSP页面负责交互展示后端Servlet与JavaBean处理业务逻辑并通过MySQL数据库持久化数据整体结构清晰适合JavaWeb课程学习与二次开发。压缩包内共有123个文件包含43个Java源代码文件、19个JSP页面、11个依赖Jar包、8个XML配置文件以及1份SQL数据库脚本同时配齐CSS、JS、图片等静态资源包大小仅5.43MB方便快速下载并导入开发环境。项目还集成Druid数据库连接池、MySQL驱动和Kaptcha验证码等常用组件目录按照功能模块进行组织便于使用者逐段阅读和调试。目前已有203人浏览学习读者不仅可以从头到尾理解一个JavaWeb项目的完整请求处理流程还能借此完成大作业的代码改造与功能扩展逐步形成具有自己风格的高质量期末项目。1. JavaWeb 在线购书管理系统期末大作业为什么选这种“老技术”反而稳每年到这个节点总有人问我要 JavaWeb 期末大作业的参考项目要求很统一能跑、有数据库、功能完整、答辩能说清。这套在线购书管理系统用的是 JSP Servlet MySQL 这套传统技术栈配 Druid 连接池和 Kaptcha 验证码外加一份可以直接导入的 SQL 脚本。它不追新但胜在结构清楚——登录鉴权、图书分类浏览、购物车、订单生成这些模块每一块代码都能在答辩时讲出设计理由而不是一句“框架自动实现的”带过。适合两类人一是计算机相关专业正在做课程设计的学生拿它当骨架去改业务比从零写省一半时间二是想补 JavaWeb 基础、想搞明白 Servlet 生命周期和会话跟踪到底怎么运作的初学者。这套项目把 MVC 分层、会话管理、连接池、验证码防刷这些必考知识点都摊开了代码量不大但每层干了什么一眼能看穿。接下来我会按“项目结构 → 数据库初始化 → 核心业务实现 → 部署运行 → 踩坑记录 → 功能扩展”的顺序拆你照着走完一遍比对着视频敲两遍有效的多。2. 项目结构与技术选型为什么是 JSP Servlet Druid 而不是 SpringBoot2.1 读懂源码包的文件布局先分清哪些是代码哪些是依赖解压后的源码包体积不大但里面混了源码、数据库脚本和依赖 jar 三类东西先看懂布局才能知道从哪儿下手。用 IDEA 导入后标准的 Maven 目录是src/main/java放业务代码src/main/webapp放 JSP 页面和静态资源db或sql目录下放初始化的 SQL 脚本。这个项目里还带着mvnw.cmd说明它是 Maven Wrapper 工程哪怕你本机没装 Maven也能用脚本拉取指定版本。依赖方面lib 目录下能看到三个关键的 jardruid-1.1.10.jar是阿里巴巴的连接池负责管理数据库连接的创建和回收mysql-connector-java-5.1.37-bin.jar是 MySQL 5.x 的 JDBC 驱动kaptcha-2.3.2.jar是 Google 的开源验证码生成库。这三个 jar 在后面配置和运行中都会遇到不要把它们当普通第三方库忽略掉。整个项目的分层很直观servlet包管请求入口service包管业务逻辑dao包管数据库操作JSP 只在 webapp 目录下做视图展示。2.2 传统 JavaWeb 架构的取舍为什么这个技术栈更适合课程设计你可能在犹豫SpringBoot 写起来不是更省事吗但期末大作业的评分标准往往更看重你对基础原理的理解而不是框架的熟练度。JSP Servlet 是 JavaWeb 的原始模型每个请求从进入 Servlet 到调用 DAO、再返回 JSP 渲染整个链路没有黑匣子。你用这套项目去答辩导师问“这个请求从浏览器发出来到数据库执行完中间经过哪些层”你带着项目代码能从头指到尾浏览器提交表单 →web.xml映射的 Servlet 接收 → service 层处理逻辑 → DAO 层拼接 SQL → MyBatis 或 JDBC 执行 → 结果存到 request 域 → forward 到 JSP 输出。每一步都是教科书级别的标准流程。Druid 连接池的选择也很典型。课程设计用不上高并发优化但 Druid 提供了监控页面和 SQL 执行统计调试的时候能直观看到每条 SQL 的执行时间这对排查 N1 查询和慢 SQL 特别有帮助。连接池的配置参数需要合理设置核心是initialSize初始连接数、maxActive最大连接数、maxWait获取连接的超时时间。初始化太小并发上来会频繁创建连接太大浪费内存课程设计的数据量给 5 到 20 就足够了。数据库脚本文件在导入后需要先执行里面包含了建库建表语句和测试数据。我的习惯是拿到项目先打开 SQL 脚本看一遍表结构搞清楚了再启动项目——很多报错其实是表名没对上或字段类型不一致造成的。执行完脚本后用 Navicat 或 IDEA 的 Database 面板确认一下book表里有没有数据没有的话登录后首页图书列表会空白容易误判成代码问题。2.3 Druid 连接池与 JDBC 驱动版本匹配的细节这个项目用的是 MySQL 5.x 和驱动 5.1.37如果你本机装的是 MySQL 8.x需要在 pom.xml 里换驱动版本否则连接会报Public Key Retrieval is not allowed错误。常见的做法是把驱动换成mysql-connector-java-8.0.33同时在 JDBC URL 后面加上allowPublicKeyRetrievaltrueuseSSLfalse。Druid 和 MySQL 8 的驱动有兼容性问题老版本的 Druid 在初始化连接时会报com.mysql.jdbc.Driver找不到实际上是因为驱动类名从com.mysql.jdbc.Driver变成了com.mysql.cj.jdbc.Driver。遇到这个错先别急着换连接池在 Druid 配置里找到driverClassName改成新驱动的类名就能跑通。3. 数据库设计与核心表结构从 SQL 脚本里读懂业务逻辑3.1 用户表、图书表、订单表的字段设计与关联关系在线购书系统的基础是用户、图书、订单三大实体外加一个关联订单和图书的中间表。从我打开这个项目的 SQL 脚本来看表的命名风格是英文小写下划线分割这在 Java 实体类映射时很友好不用处理驼峰和蛇形的转换。用户表一般包含id、username、password、phone、address这些字段密码建议存加密后的摘要而不是明文。图书表是book核心字段有书名、作者、出版社、价格、库存量和封面图路径。订单表是orders包含订单号、下单用户 ID、总金额、状态字段和创建时间。订单这块如果直接用一张表会出现大量冗余数据因为一个订单能包含多本图书。这个项目的做法是拆成订单主表和订单明细表明细表通过order_id外键关联主表同时存每本书的单价和数量。订单状态一般用0、1、2表示未支付、已支付、已发货用 int 类型比用字符串更省空间也便于状态机流转。外键约束在订单表上建议保留因为课程设计阶段数据量不大有外键能防止误删用户导致订单成孤儿数据。执行 SQL 脚本时要注意建表语句里是否有DROP TABLE IF EXISTS有的话重复执行不会报错没有的话第二次导入会因为表已存在而中断。常见做法是先执行脚本然后在命令行用SHOW TABLES命令确认建表结果不要依赖 IDE 的提示——IDE 有时候有缓存会显示旧的表结构。3.2 预置测试数据的重要性为什么脚本里要带图书数据而非空表一份合格的课程设计 SQL 脚本除了建表还要带上测试数据。空表能让项目跑起来但演示时图书列表、订单查询全是空白效果大打折扣。这个项目的脚本里预置了十几本图书的样例数据包含书名、作者、价格等登录后就能看到完整的买家视图。测试数据的写法有两种一种是逐条INSERT另一种是用INSERT INTO ... SELECT配合临时表批量生成。逐条写法可读性好适合答辩时解释每条数据对应哪个页面效果。如果你拿到手的脚本只有建表没有数据也不用慌手工INSERT几条到book表就行但要注意主键不要重复价格字段类型是DECIMAL(10,2)的话插入整数39会显示成39.00。预置数据还有一个隐藏价值——验证 SQL 语句的兼容性。MySQL 8.x 和 5.x 对AUTO_INCREMENT的语法有细微差别如果脚本是旧版本导出的在 8.x 上建表时把ENGINEInnoDB AUTO_INCREMENTx DEFAULT CHARSETutf8后面的整数去掉一般就能过。3.3 登录模块的数据库查询防止 SQL 注入的预处理语句写法登录验证是每个 JavaWeb 课程设计都绕不开的功能这个项目用的是预处理语句加参数绑定。常见做法是这样public User login(String username, String password) throws SQLException { Connection conn null; PreparedStatement ps null; ResultSet rs null; try { conn DruidUtils.getConnection(); String sql SELECT id, username, password, phone, address FROM user WHERE username ? AND password ?; ps conn.prepareStatement(sql); ps.setString(1, username); ps.setString(2, password); rs ps.executeQuery(); if (rs.next()) { User user new User(); user.setId(rs.getInt(id)); user.setUsername(rs.getString(username)); user.setPassword(rs.getString(password)); user.setPhone(rs.getString(phone)); user.setAddress(rs.getString(address)); return user; } return null; } finally { close(rs, ps, conn); } }这段代码的核心在于用PreparedStatement而不是拼字符串。?占位符配合setString参数绑定用户输入的 or 11会被当成普通字符串处理不会改变 SQL 语义。finally块里必须关闭ResultSet、PreparedStatement、Connection三个资源顺序不能乱因为连接是从 Druid 连接池借的不归还的话连接池会被耗尽。DruidUtils里一般用ThreadLocal管理连接保证一个线程内的多个 DAO 操作共享同一个事务连接。注意密码字段是明文存储这个在课程设计里常见但是个隐患后面会专门说一下怎么改成 MD5 加密存储。close方法要单独写一个工具方法判空后逐个关闭。很多人偷懒只关Connection如果用的是连接池那只是把连接还给池子ResultSet没关会导致游标泄漏长时间运行的 Tomcat 会报Too many open files。4. 核心功能实现从验证码到购物车再到订单一条链路走到底4.1 Kaptcha 验证码的集成与前端校验逻辑登录页面上的验证码图片是用 Kaptcha 生成的。Kaptcha 是一个 Java 库通过配置Producer来生成验证码文本和图片。在web.xml里配置一个 Servlet映射到/captcha.jpg前端img标签的src直接指向这个地址点击图片后拼接一个时间戳参数强制刷新就能实现“点击换一张”的效果。验证码的校验点在后端 Servlet。用户在登录表单里输入的验证码会提交到登录 Servlet和 session 里存的验证码做比对。常见做法是直接把验证码文本存在 session 的KAPTCHA_SESSION_KEY属性中判断时忽略大小写。校验失败后要立刻清除 session 里的验证码防止同一个验证码被重放请求多次尝试。后端校验的逻辑一般是String captchaInput request.getParameter(captcha); String captchaSession (String) request.getSession().getAttribute(KAPTCHA_SESSION_KEY); if (captchaInput null || !captchaInput.equalsIgnoreCase(captchaSession)) { request.setAttribute(error, 验证码错误请重新输入); request.getRequestDispatcher(login.jsp).forward(request, response); return; }这里有个容易被忽略的细节验证码过期策略。Kaptcha 默认的有效期是 120 秒超过之后 session 里的值还在但图片已经没法看了。前端点图片换一张的时候要清空输入框的值不然会出现新验证码配旧输入的尴尬。校验失败时用forward而不是sendRedirect因为要携带错误提示信息到 JSP 页面的request.getAttribute里显示。4.2 图书列表的分页查询LIMIT 参数与页面导航的数据传递首页图书列表不可能一次全查出来这个项目采用了经典的分页查询。分页涉及两个要素当前页码currentPage和每页条数pageSize。后端 Servlet 接收这两个参数查询总数得到totalCount然后计算总页数totalPages (totalCount pageSize - 1) / pageSize再用LIMIT offset, pageSize查出当前页的数据。int pageSize 8; int currentPage 1; String pageStr request.getParameter(page); if (pageStr ! null !pageStr.isEmpty()) { currentPage Integer.parseInt(pageStr); } int totalCount bookDao.getTotalCount(); int totalPages (int) Math.ceil(totalCount * 1.0 / pageSize); if (currentPage totalPages) { currentPage totalPages; } if (currentPage 1) { currentPage 1; } int offset (currentPage - 1) * pageSize; ListBook bookList bookDao.findByPage(offset, pageSize); request.setAttribute(bookList, bookList); request.setAttribute(currentPage, currentPage); request.setAttribute(totalPages, totalPages); request.getRequestDispatcher(book_list.jsp).forward(request, response);分页容易出的一个坑是页码越界。用户手输page999如果不加if判断就直接查库LIMIT 7984, 8会返回空列表页面看起来像是数据丢了。另一个坑是totalCount的查询和列表查询执行时间差——如果你在同一页面先删了一条数据再查totalPages和当前页数据就可能对不上不过课程设计场景不会遇到这种并发问题了解即可。前端页面的分页导航条用hrefbookListServlet?page${currentPage - 1}这种方式注意上一页在首页时要把按钮置灰否则会跳到第 0 页。4.3 会话跟踪与购物车实现session 作用域和对象序列化问题购物车是这个系统的核心交互用户把图书加进购物车后切换页面不能丢。这个项目用 session 存购物车这是最贴合 Servlet 规范的方案。购物车本质上是一个MapInteger, CartItemkey 是图书 IDvalue 是购物项购物项里包含图书信息和数量。加购时判断 map 里是否已有这本书有则数量加一没有则新建一项。Cart cart (Cart) session.getAttribute(cart); if (cart null) { cart new Cart(); session.setAttribute(cart, cart); } cart.addBook(bookId, book);这里有个实战中常见的坑Cart对象没有实现Serializable而 Tomcat 在正常关闭时会尝试把 session 序列化到磁盘如果项目里 session 存了不可序列化的对象关闭时会在 catalina 日志里刷NotSerializableException。虽然不影响运行但每次重启 Tomcat 都会看到红色报错答辩时容易被误认为是故障。解决办法是让Cart和CartItem实现Serializable接口。购物车加购后的数量徽标用 JSP 的${cart.totalCount}表达式直接取。注意这个totalCount是购物车里的商品种类数还是总件数要看Cart类里的实现——常见做法是遍历map.values()累加每个 item 的count属性。后端更新数量时前端可以传actionadd、actionminus、actiondelete三种参数一个 Servlet 里用 if-else 分支处理比拆三个 Servlet 更整洁。4.4 订单生成与库存扣减事务边界和 MySQL 的 InnoDB 行锁下单环节涉及两次写操作插入订单记录和扣减图书库存这两个操作必须要么同时成功要么同时失败否则会出现“订单生成了但库存没扣”或“库存扣了订单没写上”的不一致状态。这个项目的 Service 层用了事务处理核心逻辑是把Connection从 Druid 连接池借出来后setAutoCommit(false)在try-catch里做两次数据库操作全部成功再commit()任何一步失败就rollback()。库存扣减的 SQL 是有讲究的不能先查再改String sql UPDATE book SET stock stock - 1 WHERE id ? AND stock 0; int affected ps.executeUpdate(); if (affected 0) { throw new BusinessException(库存不足); }这条 SQL 用stock 0作为条件利用了数据库的行锁机制——在并发场景下两条线程同时执行这条语句只有一个能更新成功另一个affected为 0。这种写法比先SELECT stock再判断库存再UPDATE多了一步但彻底避免了超卖问题也少了一次查询的往返延迟。缺点是throw new BusinessException(库存不足)时事务要回滚不能直接让异常抛到 Servlet 层否则连接不会归还给连接池。事务回滚后要记得在finally块里恢复autoCommit为 true因为 Druid 复用连接时如果上次的事务没提交这次的查询会被锁住。经典的报错是Transaction rolled back后面跟着一大堆等待锁超时的日志排查方式就是看DruidUtils的close方法里有没有conn.setAutoCommit(true)这一行。订单号生成也是个细节。用System.currentTimeMillis()拼随机数在高并发下可能撞号但课程设计够用。更稳妥的做法是用数据库自增主键加业务前缀但实现上要多查一次表。生成订单号时不要用UUID.randomUUID().toString()直接当订单号太长了而且里面有杠号用户在订单列表里看着不像一个正经单号。5. 部署与运行的完整流程从 IDEA 到 Tomcat 到浏览器5.1 Tomcat 版本与 JDK 版本的匹配关系这个项目的代码是 Java 8 时代的产物JSP 和 Servlet 也是 javax 命名空间所以必须先确认你本机的运行环境是不是在预期范围内。最稳妥的组合是 JDK 8 Tomcat 8.5 或 Tomcat 9.0。如果你装了 JDK 17Tomcat 9 和 10 对 javax 和 jakarta 的兼容性有区别——Tomcat 10 用的是jakarta.servlet命名空间这个项目里的import javax.servlet会直接编译报错找不到包。每届都有人栽在这上面电脑里装的是 JDK 17 和新版 Tomcat 10下载了老项目打不开然后以为是代码的问题。IDEA 里配置 Tomcat 要在Run Configuration里新建一个Tomcat Server不叫Tomcat Server Local的话注意别选错。Deployment 选项卡里把项目的war exploded加进去Application context建议设成访问路径比如/bookstore。启动前检查一下 IDEA 的 Settings 里 Maven 的 JDK 编译级别是不是和项目一致否则会出现source release 8 requires target release 8这类看不懂的报错。5.2 修改数据库连接配置用户名、密码、URL 和 Druid 参数运行前必须要改的是数据库配置位置在src/main/resources下的db.properties或druid.properties。这份配置里写的数据库连接 URL、用户名和密码是项目跑起来的第一道关卡。常见的坑是这里面的密码和你本机 MySQL 不一致导致启动时Access denied for user rootlocalhost。改完配置后重启 Tomcat如果还报错有可能是 IDE 没把resources目录打进 classpath检查 Artifacts 里有没有把配置文件拷到WEB-INF/classes下。jdbc:mysql://localhost:3306/bookstore?useUnicodetruecharacterEncodingutf8这个 URL 里有几个参数要注意。useUnicodetruecharacterEncodingutf8是防止中文乱码的关键少了它插入图书表的中文书名会变成问号。如果你的 MySQL 是 8.x还要加上serverTimezoneAsia/Shanghai否则连接时差导致CST时区报错。Druid 的监控页面需要在配置里开启StatFilter一个习惯做法是让 Druid 输出慢 SQL 日志这样将来业务量大了排查问题时有个抓手。5.3 使用 Maven Wrapper 构建项目的流程与实际命令源码包里带了mvnw.cmd这是 Maven Wrapper。双击运行会出现一个自动下载 Maven 的过程国内网络如果拉取缓慢可以在 IDEA 里直接用本机的 Maven 配置跳过 Wrapper。导入项目后最简单可靠的构建方式是IDEA 右侧 Maven 面板 →clean→package等待 BUILD SUCCESS。如果package报test失败先到pom.xml里看有没有配置skipTests没有的话就在 Maven 命令里加-DskipTests。打包成功后target目录下会生成一个 war 包这个 war 包可以丢到 Tomcat 的webapps目录里部署也可以直接在 IDEA 里用 Tomcat 集成模式跑。我个人的建议是先用集成模式跑通方便调试答辩前再测一遍独立部署。因为独立部署时静态资源路径和数据库连接池初始化时机都稍有不同提前测完不会在演示中途才冒出来问题。创建的 war 包如果部署时出现SEVERE: Exception sending context initialized event大概率是context.xml或log4j.properties里的配置有旧路径去WEB-INF/classes下确认配置文件有没有被正确过滤。6. 避坑指南期末大作业从搬运到答辩的常见问题排查6.1 报错ClassNotFoundException: com.mysql.jdbc.Driver现象Tomcat 启动时报找不到驱动类项目完全起不来。原因驱动 jar 没有被打进最终的部署产物或者驱动类名配置不对。Maven 项目里依赖声明为provided作用域时jar 不会出现在WEB-INF/lib里运行时自然找不到类。解决打开 pom.xml 检查依赖作用域把scope改成compile如果版本是 8.x 驱动把配置里的driverClassName改成com.mysql.cj.jdbc.Driver同时确认 target 目录下WEB-INF/lib里有对应 jar。在 IDEA 里跑的话还要在 Artifacts 的Available Elements里右键依赖选择Put into /WEB-INF/lib。6.2 登录时验证码图片不显示现象登录页其他元素正常就验证码那张图是裂开的。原因Kaptcha 的 Servlet 没有注册或者配置的路径和前端img标签对不上。也有可能是请求验证码图片时 session 没建立Kaptcha 无法把验证码文本存到 session 里。解决打开web.xml确认 Kaptcha Servlet 的url-pattern和前端src请求地址完全一致包括大小写。再看生成验证码的 Bean 是否有WebServlet注解同时存在配了注解又配了web.xml会冲突导致服务启动异常。验证码图片不显示时用浏览器直接访问验证码 URL能出图就说明后端没问题定位到前端。6.3 下单后订单生成了但库存没扣减现象页面上订单列表能看到新订单但book表的库存数量没变化。原因订单插入和库存更新没有放在同一个事务里。典型的写法是两个 DAO 方法各自从连接池拿连接、各自commit导致第一个操作成功第二个失败时无法回滚。解决在 Service 层把两个 DAO 调用包在一个事务方法里从DruidUtils.getConnection()拿到连接后设setAutoCommit(false)两个 DAO 方法重载为传入 Connection 参数。等两个操作都成功后再commit()捕获异常时rollback()最后在finally里归还连接。检查你的 DAO 方法签名如果还是public void insertOrder(Order order)而没有 Connection 参数基本就是这个原因。6.4 买完书后库存变成负数现象下单过程中库存量变成负数图书列表出现 0 或者负值的库存显示。原因库存更新的 SQL 是UPDATE book SET stock stock - 1没有加AND stock 0的条件。并发下单时多个请求同时读到同样的库存值然后都做了减一操作导致超卖。解决把UPDATE语句改成UPDATE book SET stock stock - 1 WHERE id ? AND stock 0执行后检查affected是否为 0为 0 抛异常回滚。这条改法用数据库行锁保证原子性比先查库存再更新要稳妥而且代码改动量只有一行 SQL答辩时还能顺带讲一下乐观锁的区别。6.5 JSP 页面中文全部乱码现象浏览器里图书名称、订单地址显示为问号或者乱码。原因可能有三层问题一是 MySQL 数据库编码不是 utf8二是 JDBC URL 没加characterEncoding三是 JSP 页面本身的pageEncoding不对。解决先用SHOW CREATE DATABASE bookstore查数据库默认字符集不是 utf8 就改库改表。确认 JDBC URL 加了useUnicodetruecharacterEncodingutf8。最后检查 JSP 文件第一行的pageEncoding是否统一写成 utf-8。陈旧的response.setContentType写法改成text/html;charsetutf-8。6.6 分页跳转时当前页码超过总页数现象点下一页翻到最后一页之后再点下一页列表空白或者显示 0 条数据。原因Servlet 只接收了page参数没判断它的上下边界。用户直接改 URL 里的page999offset计算出超大值SQL 查询自然返回空。解决加一个currentPage的边界裁剪逻辑。如果currentPage大于totalPages把它改成totalPages小于 1 改成 1。这个判断要放在分页查询 SQL 执行之前。7. 扩展与验证给期末项目加分的三个改造方向和自查方法7.1 密码明文改 MD5 加盐存储的改造步骤拿到手的项目如果密码是明文存储在 user 表里这在答辩时是明显的安全缺陷。改造方向是把密码改成 MD5 加盐的摘要存储。所谓加盐就是在密码原文后面拼接一个随机字符串再哈希防止两本书用户密码相同时摘要也完全相同。注册时生成一个UUID前八位作为盐存到user表的salt字段再把MD5(password salt)存到password字段。登录时用前端提交的密码和数据库里查到的 salt 拼起来算 MD5比对结果一致才放行。public static String md5WithSalt(String password, String salt) { String source password salt; StringBuilder sb new StringBuilder(); try { MessageDigest md MessageDigest.getInstance(MD5); md.update(source.getBytes(StandardCharsets.UTF_8)); byte[] bytes md.digest(); for (byte b : bytes) { sb.append(String.format(%02x, b)); } } catch (NoSuchAlgorithmException e) { throw new RuntimeException(MD5 algorithm not found, e); } return sb.toString(); }这段代码的核心逻辑是MessageDigest.getInstance(MD5)拿到摘要实例update写入待加密的字节数组digest()得到 16 字节的结果再用String.format(%02x)转成 32 位十六进制字符串。注意source.getBytes必须指定 UTF-8 编码否则跨平台时默认字符集不一致会得到不同的摘要。改造后需要在注册 Servlet 和登录 Servlet 两个地方同时改注册时多一步生成盐的逻辑登录时不再直接用WHERE username ? AND password ?而是先按用户名查用户拿到 salt 后算摘要再比对。7.2 图书搜索功能的模糊查询实现给图书列表加一个搜索框按书名或作者模糊查询这个改动量小而演示效果好。后端 Servlet 接收keyword参数后在 SQL 语句里用WHERE book_name LIKE ?或者OR author LIKE ?配合setString(1, % keyword %)实现。注意不是直接拼LIKE % keyword %那样既会被注入又容易在关键字带单引号时爆语法错误。String sql SELECT * FROM book WHERE book_name LIKE ? OR author LIKE ?; ps conn.prepareStatement(sql); ps.setString(1, % keyword %); ps.setString(2, % keyword %);一个值得讲的细节是 LIKE 的%通配符应该放在参数里还是 SQL 里。放参数里的好处是即便输入的关键字本身包含%也只是匹配所有数据不会让 SQL 语法出错。如果放 SQL 里传入参数时用setString(keyword)那就变成了精确匹配搜索功能就失效了。搜索和分页一起实现时搜索条件要带在分页的 SQL 里并且翻页链接上也要带上keyword${keyword}不然你搜索了书名点下一页结果又变成全部图书。URL 的完整参数拼接建议用URLEncoder.encode(keyword, UTF-8)处理特殊字符。7.3 答辩前必做的五个验证动作项目跑到能演示只是第一步答辩现场经常出现“在自己电脑上全好换个环境就翻车”的情况。我总结了一个五分钟自查清单你演示前照着过一遍。第一注销登录后重启 Tomcat确认未登录状态下访问购物车或订单页面会被拦截器重定向到登录页这验证了会话过滤器的有效性。第二在 MySQL 命令行手动执行一遍初始化脚本确认能从零建库防止答辩评委要求现场演示导入。第三用浏览器的开发者工具看网络请求列表确认没有红字请求——有些静态资源 404 在页面上不易察觉但评委可能会翻看一眼。第四用手机浏览器访问系统主机的局域网 IP演示响应式布局是否正常这个不是必测项但做了会被认为有工程意识。第五把 Druid 的maxActive临时改成 2 模拟连接池较小的情况触发一次并发访问确认不会因为连接耗尽直接崩掉。从那以后每接手一个 JavaWeb 课程设计项目我都会强制走一遍“清缓存跑一次、删库跑一次、换端口跑一次”的流程答辩时心里才踏实。这份在线购书管理系统的源码包核心价值在于它的模块边界清晰无论你拿去改代码还是照着敲都有足够的参考意义。希望帮到你。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询