新闻详情

新闻详情

首页 / 资讯中心 / 详情

JavaWeb网上书店系统从源码到部署:Servlet JSP与MySQL实战指南

发布时间:2026/9/28 15:32:14来源:尧图网络
JavaWeb网上书店系统从源码到部署:Servlet JSP与MySQL实战指南
简介这是一份基于JavaWeb的网上书店系统完整项目面向高校计算机相关专业学生尤其适合课程设计、期末大作业等场景。项目已获导师指导并取得97分高分下载后可直接运行无需额外修改。压缩包共108个文件约6.75MB包含31个Java源码文件、29个JSP页面、13个XML配置以及SQL数据库脚本同时附带CSS样式和图片素材覆盖前端展示、后端控制与数据持久化各层面。已有1709人学习下载。通过该资源可掌握Servlet、JSP、MySQL等核心技术的综合运用参考完整的项目结构、数据库设计及前后端交互实现是快速完成同类作业或入门JavaWeb开发的实用参考。1. 一份 javaweb 网上书店源码数据库压缩包期末周最该先做的事是什么期末周拿到一份“基于javaweb的网上书店系统源码数据库期末大作业.zip”很多人的第一反应是打开 IDEA 双击运行然后被红叉、404、ClassNotFoundException 淹没了周末。这个压缩包代表了一类非常典型的 JavaWeb 课程设计交付形态ServletJSP 后端源码、一份 SQL 数据库脚本、若干前端页面外加给老师演示用的搭建说明。它的价值不在于代码写得多花哨而在于让你用最短时间看到一个完整的 JavaWeb 项目长什么样——从浏览器请求到 Servlet 控制器再到 JDBC 操作 MySQL整条链路是通的。适合两类人期末要交大作业的学生以及想找一个“能跑通的最小完整案例”来对照课堂知识的新手。记住一个原则先让它跑起来再谈看懂和改造。2. 先看懂项目再动手JavaWeb 网上书店的分层结构与数据库设计2.1 为什么期末大作业都长这样JSPServletMySQL 技术栈选型逻辑这类网上书店系统几乎清一色是 JSPServletMySQL很少见到用 SpringBoot 写的。不是大家不会是期末大作业的考核点和 SpringBoot 的封装方式天然错位。JavaWeb 课程教材的主线是 Servlet、JSP、Filter、JDBC 这些基础组件老师答辩时问的是“一个请求从浏览器到数据库再回来中间经历了什么”。用 SpringBoot 写请求被 DispatcherServlet 接住、参数被自动绑定、数据库操作被 MyBatis 代理学生很难把链路讲清楚。JSPServlet 则相反每一层都在代码里明摆着JSP 负责展示Servlet 负责接收请求和跳转DAO 类里是手写的 SQLConnection 是自己从 DriverManager 拿的。从工程角度看这个选型也足够轻。不需要 Maven 管理依赖下载个 zip 解压把 jar 扔进 WEB-INF/lib 就能跑不需要理解 IOC 容器、AOP 代理这些抽象概念一个项目跑通了Servlet 生命周期、请求转发与重定向、Session 保持、事务提交回滚这些考点全都能对上号。我用一张表对比过三种方案适合期末场景的结论很明确技术栈课程知识点覆盖答辩讲底层环境要求适合场景JSPServletJDBC覆盖教材全部主线每一层都能展开讲JDKTomcatMySQL课程设计、期末大作业SSMSpringSpringMVCMyBatis进阶框架课需解释IOC、AOP、ORM原理建议Maven管理毕业设计、简历项目SpringBoot封装过深容易卡在“自动配置”Maven内嵌Tomcat实习、企业开发网上书店这个选题本身也是把 JavaWeb 的知识点串得很满的一个题目。登录注册覆盖表单和 Session图书列表覆盖查询和分页购物车覆盖 Session 复杂对象存取下单覆盖事务后台管理覆盖 Filter 拦截和增删改查。一次大作业做完等于把整本书的练习都过了一遍。所以我一直觉得这个题目不是简单是典型——它把课程里每个独立的知识点用一条业务线串了起来。2.2 解压后先看这几个文件源码目录、SQL脚本、lib依赖的对应关系拿到 zip 别急着启动先解压扫一眼结构。这类项目不管名字怎么起目录骨架高度相似。我见过最常见的布局是这样online-bookstore/ ├── src/ # Java 源码根目录 │ ├── com/bookstore/dao/ # 数据访问层UserDao.java、BookDao.java、OrderDao.java │ ├── com/bookstore/entity/ # 实体类User.java、Book.java、Order.java、CartItem.java │ ├── com/bookstore/servlet/ # 控制器层LoginServlet.java、CartServlet.java、AdminServlet.java │ ├── com/bookstore/filter/ # 过滤器EncodingFilter.java、AdminFilter.java │ ├── com/bookstore/util/ # 工具类DBUtil.java数据库连接、MD5Util.java │ └── db.properties # 数据库连接配置也可能写死在 DBUtil 里 ├── web/ # Web 根目录也叫 WebContent 或 webapp │ ├── index.jsp / login.jsp / register.jsp / book_list.jsp │ ├── admin/ # 后台管理页面admin/book_list.jsp、admin/order_list.jsp │ ├── css/ js/ images/ │ └── WEB-INF/ │ ├── web.xml # 部署描述文件Servlet/Filter 映射全在这里 │ └── lib/ # 依赖 jar 包mysql-connector-java、jstl、standard └── bookstore.sql # 数据库脚本建表语句 初始测试数据重点看三个位置。第一是util/DBUtil.java或db.properties这是数据库连接的入口后面第 3 章要改的密码就在这里第二是WEB-INF/lib里面必须有mysql-connector-java的 jar没有它在启动阶段就会报驱动找不到第三是bookstore.sql打开它看开头是不是包含CREATE DATABASE语句这决定了导入数据库时要不要手动先建库。如果脚本里已经写了建库逻辑你只需要在 MySQL 里执行整个文件如果只有建表语句就得先自己建库再导入。提示花十分钟把 src 目录下的类名都扫一遍。一个典型的网上书店系统类数量通常在 15 到 25 个之间。如果连一个实体类和 DAO 都没有只有一堆 JSP那这个项目大概率是表面工程跑通了也经不起答辩追问。2.3 五张表就能撑起一个书店用户、分类、图书、订单、订单项怎么设计网上书店的数据库设计比想象中简单核心就五张表。我把它整理成一套可以直接对照理解的标准结构-- 用户表前台用户和后台管理员共用 CREATE TABLE t_user ( uid INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(64) NOT NULL, -- 建议存 MD5别存明文 role TINYINT DEFAULT 0, -- 0普通用户1管理员 email VARCHAR(100) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 图书分类表 CREATE TABLE t_category ( cid INT PRIMARY KEY AUTO_INCREMENT, cname VARCHAR(50) NOT NULL ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 图书表分类是一对多关系里的“一” CREATE TABLE t_book ( bid INT PRIMARY KEY AUTO_INCREMENT, bname VARCHAR(100) NOT NULL, price DECIMAL(10,2) NOT NULL, stock INT NOT NULL DEFAULT 0, image VARCHAR(255), cid INT, -- 外键指向分类表 CONSTRAINT fk_book_category FOREIGN KEY (cid) REFERENCES t_category(cid) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 订单表用户一次下单的主记录 CREATE TABLE t_order ( oid INT PRIMARY KEY AUTO_INCREMENT, orderno VARCHAR(32) NOT NULL, -- 业务订单号页面展示用 uid INT NOT NULL, -- 下单用户 total_price DECIMAL(10,2) NOT NULL, status TINYINT DEFAULT 0, -- 0未发货 1已发货 2已完成 create_time DATETIME DEFAULT CURRENT_TIMESTAMP, CONSTRAINT fk_order_user FOREIGN KEY (uid) REFERENCES t_user(uid) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 订单项表每个订单里买了哪些书、买了几本、当时什么价 CREATE TABLE t_order_item ( iid INT PRIMARY KEY AUTO_INCREMENT, oid INT NOT NULL, -- 外键指向订单表 bid INT NOT NULL, book_name VARCHAR(100), -- 冗余字段保存下单时的书名 price DECIMAL(10,2), -- 冗余字段保存下单时的单价 quantity INT NOT NULL, CONSTRAINT fk_item_order FOREIGN KEY (oid) REFERENCES t_order(oid) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;这个设计里有两个值得在答辩时主动讲的点。第一个是t_order_item里为什么要冗余book_name和price而不是下单时只存bid、查的时候去关联t_book表。原因是图书的价格会调整、书名可能改、图书甚至会被下架删除如果订单明细去关联实时表历史订单的数据就跟着变了。下单时把书名和单价快照一份订单才是真实的历史凭证。第二个是订单状态用TINYINT存数字而不是直接存中文业务上 0、1、2 三个数字在代码里用常量或枚举解释数据库层面只存最小数据类型查询和比较都更快。t_user表里的role字段决定了谁能进后台。普通用户注册时role默认 0管理员账号一般由 SQL 脚本预置一条role1的数据用户名和密码写在bookstore.sql末尾的 INSERT 语句里。你打开脚本搜一下INSERT INTO t_user就能找到这组账号是登录后台管理页面的钥匙后面第 5 章排查登录问题也会用到它。3. 在 IDEA 里把网上书店跑起来运行 JavaWeb 项目的完整配置与三个必调参数3.1 环境版本怎么选JDK 8、Tomcat 8.5、MySQL 5.7 的搭配理由配置 JavaWeb 项目最容易翻车的不是代码而是版本组合。很多期末项目是两三年前写的用的还是javax.servlet包而现在新装的 JDK 17 和 Tomcat 10 已经把命名空间迁到了jakarta.servlet老代码在编译期就直接报“程序包 javax.servlet 不存在”。这不是代码问题是生态升级砍掉了老兼容。所以我的原则是不要用最新版用最稳的组合。组件推荐版本原因JDK1.88u202 及以上老项目源码基本是 JDK8 语法JDK17 会遇到 javax 到 jakarta 的迁移问题Tomcat8.5.x对应 Servlet 3.1 / JSP 2.3 规范与教材和旧 jar 完全兼容MySQL5.7 或 8.05.7 最省心8.0 需要配套换 JDBC 驱动和驱动类名IDEIDEA Community免费且内置 Tomcat 集成省去 Eclipse 的配置麻烦这套搭配的玄学在于只要版本落在这个区间里项目跑不起来的锅基本都能甩给配置而不是代码。如果你电脑上已经装了 JDK 17不用卸载在 IDEA 的 Project Structure 里单独给这个项目指定 JDK 8 即可。MySQL 8.0 也能用但要注意两点驱动 jar 必须是mysql-connector-java-8.x驱动类名要写com.mysql.cj.jdbc.DriverURL 里最好带上serverTimezoneAsia/Shanghai否则报时区错误。5.7 没这些讲究所以做课程设计我一般直接建议上 5.7。3.2 从解压到首页IDEA 导入、Tomcat 部署、数据库导入三步走环境装好后按下面这条路径走十分钟内应该能看到首页。每一步都拆开说卡在哪一步就回来核对哪一步。第一步解压并打开项目。注意 zip 解压后可能是双层目录比如online-bookstore/online-bookstore/把内层目录移动出来再打开。IDEA 里用File → Open选中源码根目录含src和web/WEB-INF/web.xml的那个目录IDEA 会自动识别成 Web 项目。识别失败的判断标准是Project 面板里web目录没有蓝色小圆点标记说明没被识别成 Web 根目录需要手动在Project Structure → Facets里补一个 Web Facet指定 Web 资源目录和 web.xml 路径。第二步配置 Project SDK。打开File → Project Structure → Project把 SDK 选成 1.8。再切到Modules确认src被标记为 Sources蓝色web被标记为 Web 资源目录。这一步错了后面编译会报“package com.bookstore.dao does not exist”。第三步添加依赖 jar。在 Project 面板里找到WEB-INF/lib目录右键Add as Library把 lib 下所有 jar 加入项目依赖。如果lib目录是空的那就是原 zip 漏了依赖包需要自己补mysql-connector-java和 JSTL 相关 jar这个放到第 5 章排查里细说。第四步配置 Tomcat。打开Run → Edit Configurations点选择Tomcat Server → Local在 Application server 里指向你本地的 Tomcat 安装目录。切到Deployment选项卡点 → Artifact选项目生成的war exploded包。这一步最关键的是Application context里的路径填/bookstore启动后访问地址就是http://localhost:8080/bookstore/。第五步导入数据库。先看bookstore.sql开头有没有CREATE DATABASE。有的话直接在 MySQL 命令行或 IDEA 的 Database 面板里执行整个脚本没有的话先手动建库再导表CREATE DATABASE IF NOT EXISTS bookstore DEFAULT CHARACTER SET utf8mb4; USE bookstore; source /你的路径/bookstore.sql;source是 mysql 命令行的导入指令路径不要用反斜杠Windows 下也建议写成/你的路径/bookstore.sql这种斜杠形式否则容易转义出错。导入成功后用SHOW TABLES;验证能看到至少五张表才算成功。第六步启动。点绿色运行按钮控制台出现Server startup in xxx ms后浏览器打开http://localhost:8080/bookstore/。如果这里显示 404优先检查第四步的Application context是不是配置成了别的前缀或者 web.xml 里没有配置 welcome-file。如果直接报端口占用见第 5 章 5.3 节。3.3 必调的三个参数数据库连接、请求字符集、部署上下文路径跑起来之前有三个参数是每次接手新项目都要核对一遍的。第一个参数JDBC 连接配置。打开util/DBUtil.java或db.properties改成你能连上 MySQL 的真实账号// DBUtil.java 中典型的 JDBC 连接代码 public class DBUtil { private static String url jdbc:mysql://localhost:3306/bookstore?useSSLfalsecharacterEncodingutf8serverTimezoneAsia/Shanghai; private static String user root; private static String password 123456; // 改成你的 MySQL 密码 public static Connection getConnection() throws Exception { Class.forName(com.mysql.jdbc.Driver); // MySQL 8.0 需改为 com.mysql.cj.jdbc.Driver return DriverManager.getConnection(url, user, password); } }URL 里localhost:3306是数据库地址和端口bookstore是库名必须与 3.2 步建的库名一致。characterEncodingutf8负责连接层字符集serverTimezone是 MySQL 8.0 的强制要求5.7 可以不加。密码不对的话第一个请求就会报Access denied for user rootlocalhost。第二个参数请求字符集。如果登录后输入中文用户名跳转后变乱码大多是没设置请求编码。常见做法是在web.xml里配置一个全局字符集过滤器filter filter-nameencoding/filter-name filter-classcom.bookstore.filter.EncodingFilter/filter-class init-param param-nameencoding/param-name param-valueUTF-8/param-value /init-param /filter filter-mapping filter-nameencoding/filter-name url-pattern/*/url-pattern /filter-mapping对应的 EncodingFilter 里执行request.setCharacterEncoding(encoding)和response.setCharacterEncoding(encoding)。注意url-pattern要写成/*只写/的话只拦截根路径表单提交照样乱码。第三个参数部署上下文路径。就是这个系统的访问根路径。IDEA 的 Tomcat 配置里Application context填了/bookstore那页面里所有链接都要以/bookstore开头或者在 JSP 里用${pageContext.request.contextPath}动态拼接。很多项目 CSS 样式全挂、图片裂开不是代码问题是 JSP 里写了/css/style.css这种绝对路径而项目部署在/bookstore下面。解决办法是用 JSTL 的c:url value/css/style.css/自动带上上下文路径或者统一用${pageContext.request.contextPath}拼前缀。提示如果你改完配置后怎么调都还是不对把 IDEA 的 Run 配置里的 Tomcat 删掉重新加一次。IDEA 的 Tomcat 集成偶尔会缓存 artifact 状态重建一个配置往往就正常了。4. 网上书店核心功能拆解登录注册、购物车、后台管理的实现链路4.1 登录注册与 Session 保持从表单提交到 redirect 的完整一次请求整个系统的入口是登录。login.jsp 里一个表单 POST 到/loginLoginServlet 的doPost方法接住请求。下面这段代码是这类项目的标准写法WebServlet(/login) // 老项目也可能在 web.xml 里配置映射两者取其一 public class LoginServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding(UTF-8); String username request.getParameter(username); String password request.getParameter(password); UserDao dao new UserDao(); User user dao.findByUsernameAndPassword(username, password); if (user ! null) { HttpSession session request.getSession(); session.setAttribute(loginUser, user); // 登录状态写进 Session response.sendRedirect(request.getContextPath() /index.jsp); // 重定向 } else { request.setAttribute(msg, 用户名或密码错误); request.getRequestDispatcher(/login.jsp).forward(request, response); // 转发 } } }这里有两个初学者最容易踩的点。第一个是getParameter(username)里的字符串必须和表单里input nameusername的name属性完全一致差一个字母查出来的就是 null。第二个是登录成功用sendRedirect失败用forward。转发是服务器内部的跳转浏览器地址栏不会变用户刷新页面时表单会再次提交登录操作重复执行重定向是服务器返回 302浏览器重新向新地址发一次 GET 请求地址栏变成index.jsp刷新也不会重复提交。这个区别答辩时老师基本必问你要能说出来。Session 保持的原理是Tomcat 在用户第一次访问时生成一个JSESSIONID的 Cookie后续请求都带着这个 Cookie服务器据此找到对应的 HttpSession 对象。登录成功后的 user 对象存在 Session 里JSP 页面用 EL 表达式${loginUser.username}就能显示当前用户名。访客没登录时这个值为 null页面里用 JSTL 的c:if test${loginUser ! null}做分支展示。注册功能比登录多两步先SELECT COUNT(*)检查用户名是否已存在存在就回显“用户名已被注册”不存在才执行 INSERT。密码存储建议用MD5(password)加密哪怕不加密也能跑但加密是答辩时的主动加分项。4.2 购物车与下单Session 里的购物车怎么变成数据库里的订单网上书店的购物车一般不落数据库而是存在 Session 里。结构通常是MapInteger, CartItemkey 是图书 idvalue 是“一个商品 数量”的封装。加入购物车的逻辑就几行// 加入购物车的核心逻辑CartServlet 的 doGet 中的分支 HttpSession session request.getSession(); MapInteger, CartItem cart (MapInteger, CartItem) session.getAttribute(cart); if (cart null) { cart new HashMap(); session.setAttribute(cart, cart); // 第一次加购先创建购物车 } int bid Integer.parseInt(request.getParameter(bid)); Book book bookDao.findById(bid); CartItem item cart.get(bid); if (item ! null) { item.setQuantity(item.getQuantity() 1); // 已有同款数量加 1 } else { cart.put(bid, new CartItem(book, 1)); // 新商品数量初始为 1 } response.sendRedirect(request.getContextPath() /cart.jsp);注意session.getAttribute(cart)取出来强转 Map 时要判空因为用户第一次访问时 Session 里根本没有这个 key。这个判空逻辑漏了一加购就报 NullPointerException。购物车放 Session 而非数据库原因是用户可能还没登录就把书放进去了提前建表会制造一堆没人认领的脏数据。真正的重头戏是下单。用户点击结算系统要做三件事写订单表、写订单项表、清空购物车。前两件事必须在一个事务里完成否则会出现“有订单主记录、没有订单明细”的脏数据。核心代码是这个样子public void createOrder(Order order, ListOrderItem items) { Connection conn null; try { conn DBUtil.getConnection(); conn.setAutoCommit(false); // 开启事务两条 INSERT 同生共死 String sqlOrder INSERT INTO t_order(orderno, uid, total_price, status) VALUES(?,?,?,0); PreparedStatement ps conn.prepareStatement(sqlOrder, Statement.RETURN_GENERATED_KEYS); // ps.set... 填充订单号、用户ID、总金额此处省略具体参数 ps.executeUpdate(); ResultSet keys ps.getGeneratedKeys(); // 拿到数据库自增生成的订单主键 oid keys.next(); int oid keys.getInt(1); String sqlItem INSERT INTO t_order_item(oid,bid,book_name,price,quantity) VALUES(?,?,?,?,?); PreparedStatement ps2 conn.prepareStatement(sqlItem); for (OrderItem item : items) { ps2.setInt(1, oid); ps2.setInt(2, item.getBid()); ps2.setString(3, item.getBookName()); ps2.setBigDecimal(4, item.getPrice()); ps2.setInt(5, item.getQuantity()); ps2.addBatch(); // 批量攒着最后一起执行 } ps2.executeBatch(); conn.commit(); // 全部成功提交 } catch (Exception e) { if (conn ! null) { conn.rollback(); } // 任何一步失败全部回滚 throw new RuntimeException(下单失败, e); } finally { // 关闭 PreparedStatement、ResultSet、Connection并把 autoCommit 恢复为 true } }这段代码里有三个设计点是往年学生容易忽略的一是Statement.RETURN_GENERATED_KEYS订单表的主键是数据库自增的执行 INSERT 后要用getGeneratedKeys()才能拿到这个 oid否则订单项没法关联主订单二是addBatch()批量执行订单项可能有七八条一条条executeUpdate也不是不行但批量执行在数据量上来后效率差异明显写出来也显得你对 JDBC 有完整认识三是事务边界setAutoCommit(false)之后所有操作都在同一个事务里只有commit()才真正落库中途任何一步抛异常都用rollback()撤销全部。事务是数据库课程的核心考点这个项目里下单是唯一必须用事务的场景。下单成功后的收尾动作清空 Session 里的购物车session.removeAttribute(cart)然后跳转到一个订单成功页面页面里显示订单号。订单号建议用时间戳加随机数生成比如yyyyMMddHHmmss加三位随机数字不要在数据库里用自增 id 直接当订单号展示给用户业务单号和主键要分开。4.3 后台管理的增删改查一个 Filter 拦截出来的管理后台后台管理模块和前台共用一套登录体系区别在于权限。实现方式是用一个 Filter 拦截所有/admin/*路径WebFilter(/admin/*) public class AdminFilter implements Filter { public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { HttpServletRequest request (HttpServletRequest) req; HttpServletResponse response (HttpServletResponse) resp; HttpSession session request.getSession(); User user (User) session.getAttribute(loginUser); if (user ! null user.getRole() 1) { // role1 才是管理员 chain.doFilter(req, resp); // 放行继续请求后续处理 } else { response.sendRedirect(request.getContextPath() /login.jsp); } } }Filter 是 JavaWeb 里非常接近 AOP 思想的组件只不过它作用在 URL 层面。加了这一个拦截器admin/下的所有 JSP 和 Servlet 都自动获得身份校验不需要在每个后台页面里重复写“判断是否登录”的代码。这既是代码复用也是答辩时能讲清楚的一个标准点。后台管理的内容就是标准的增删改查。图书管理的四个核心操作在 BookDao 里对应四个方法findAll()用SELECT * FROM t_book JOIN t_category查出列表findById(bid)供编辑页回显add(book)执行 INSERTupdate(book)执行 UPDATE。删除稍微特殊如果图书被订单项引用外键约束会挡住 DELETE直接抛异常。常见的处理方式是两种删除前先查t_order_item有没有引用有就提示“该图书存在订单记录不能删除”或者改逻辑删除给t_book加一个is_delete字段删除时 UPDATE 成 1查询时默认过滤掉。第二种更符合真实电商做法也是对前面t_order_item冗余设计的一个呼应——历史订单需要保留完整的商品快照图书本身可以下架但不该物理消失。5. 网上书店系统运行常见的 5 个坑期末周最容易翻车的环节排查5.1 启动就报 ClassNotFoundException驱动 jar 没进 WEB-INF/lib现象Tomcat 启动成功但一执行登录或查询图书就抛java.lang.ClassNotFoundException: com.mysql.jdbc.Driver或者控制台提示Cannot load JDBC driver class com.mysql.jdbc.Driver。原因mysql-connector-java.jar虽然在 IDEA 的 Project Structure 里加了 Libraries但没被打进部署用的 artifact。IDEA 里“添加依赖”和“打包进 war”是两件独立的事前者只影响编译期后者决定运行期 Tomcat 能不能在 WEB-INF/lib 里找到这个类。很多新手在 Libraries 里加了 jar编译没问题一运行就挂就是这个原因。解决打开File → Project Structure → Artifacts选中当前用的war exploded看右侧 Output Layout 里有没有lib目录。没有就在 WEB-INF 下新建 lib然后点 → Library Files把 mysql 驱动加进去确认它出现在WEB-INF/lib下。改完重启 Tomcat。一个快速验证方法直接到 Tomcat 解压目录的webapps/ROOT/WEB-INF/lib下看这个 jar 在不在如果项目跑的是 exploded 模式这个目录会被 IDEA 同步更新。服务器上部署 war 包时同理jar 必须存在 war 包内WEB-INF/lib路径下。5.2 中文乱码连环翻车JSP、请求、数据库三个字符集环节检查现象打开页面中文全是乱码或者页面显示正常但往数据库插入的中文变成了??。这两个现象经常同时出现排查时很容易只修了页面、漏了数据库。原因三个环节的字符集必须一致——JSP 文件本身编码、浏览器请求编码、数据库连接和表编码。最常见的翻车组合是JSP 的pageEncoding声明了 UTF-8但文件实际是 IDEA 用 GBK 存的表单 POST 请求没设置request.setCharacterEncoding(UTF-8)JDBC URL 没带characterEncodingutf8。三个环节只要有任何一个不一致中文就保不住。解决按顺序堵漏。第一JSP 首行必须声明% page contentTypetext/html; charsetUTF-8 pageEncodingUTF-8 %同时在 IDEA 右下角确认文件编码是 UTF-8。第二按前面 3.3 节的方式配置 EncodingFilter 或至少在 Servlet 的doPost开头执行request.setCharacterEncoding(UTF-8)。第三JDBC URL 加上characterEncodingutf8。第四检查数据库表和库本身的字符集登录 MySQL 执行SHOW CREATE TABLE t_book;如果 DEFAULT CHARSET 不是 utf8mb4用ALTER TABLE t_book CONVERT TO CHARACTER SET utf8mb4;修正。最后把 MySQL 服务端默认字符集也确认一下SHOW VARIABLES LIKE character_set_server;。排查完这四处中文乱码基本绝迹。5.3 Tomcat 起不来或页面 404端口占用和 artifact 打包核对现象启动 Tomcat 时控制台红字报Port 8080 was already in use然后启动自动停止或者 Tomcat 正常启动但浏览器访问http://localhost:8080/bookstore/直接 404。原因端口占用通常是之前开发过一次没关干净或者别的程序占用了 8080404 的场景更复杂常见的有三种部署上下文路径配置的和访问的不一致、web.xml 没配置 welcome-file、artifact 打包漏了页面资源。这三种情况表现都是 404但排查方向完全不一样。解决端口占用Windows 下用命令查netstat -ano | findstr :8080 taskkill /F /PID 这里填查到的进程号也可以在 Tomcat 安装目录的conf/server.xml里把端口改成 8081但改端口后 IDEA 的 HTTP port 设置也要同步不然 IDEA 和 Tomcat 各说各话。404 排查顺序是先看 IDEA 的 Deployment 里Application context填的是什么填了/bookstore就访问/bookstore/再看web.xml里有没有welcome-fileindex.jsp/welcome-file没有就手动在地址栏补上完整页面路径最后看 Artifacts 的 Output Layout 里web根目录下有没有WEB-INF和所有 JSP。如果 artifact 里只有 classes 没有页面Tomcat 起了也等于空壳。调试阶段用war exploded比war稳war 模式每次修改都要重新 buildexploded 是解包目录改动即时同步。5.4 页面开得起来但数据不显示EL 表达式、JSTL 依赖逐个查现象登录成功进入首页页面框架都在但图书列表表格是空的或者整个页面报 500控制台出现Unable to find tag library或java.lang.NoClassDefFoundError: javax/servlet/jsp/jstl/core/LoopTag。原因前者是数据没从 Servlet 传到页面典型代码错误是 Servlet 里把 list 存成了局部变量没有request.setAttribute(bookList, list)后者是 JSP 用了c:forEach标签但WEB-INF/lib里没有 JSTL 的 jar。JSTL 需要两个 jarjstl.jar和standard.jar少一个页面都渲染不了。解决先区分是数据问题还是标签问题。在 JSP 页面上临时加一行${bookList}如果输出null或空数组说明数据没传到位回 Servlet 检查 setAttribute 的 key 名和 JSP 里取的名字是否一致这是最容易犯的低级错误——Servlet 里写setAttribute(bookList, list)JSP 里写${bookList}两个名字只要有一个字母不一致页面就是空白。如果输出了数组内容但表格还是空说明 JSTL 标签解析失败检查 lib 目录里同时有jstl.jar和standard.jar并且在 JSP 首行加了% taglib prefixc urihttp://java.sun.com/jsp/jstl/core %老项目常用这个 URI新版本 JSTL 也有用jakarta.tags.core的但配合 Tomcat 8.5 和 JDK8 的话http://java.sun.com/jsp/jstl/core是兼容范围。页面里c:forEach的items属性指向你 setAttribute 的 keyvar变量名在循环体内引用这两处也对不上号的话表格同样空白。5.5 换台电脑就连不上数据库JDBC URL 里的 localhost 别写死现象在自己的电脑上一切正常把项目拷到教室电脑或者在老师演示用的机器上部署启动后一访问就报Communications link failure或Access denied。原因DBUtil.java里的 URL 写死了jdbc:mysql://localhost:3306/bookstore。localhost指向的是运行 Tomcat 的这台机器本身。在你自己电脑上Tomcat 和 MySQL 在同一台机器没问题换到另一台机器如果数据库还是原来那台的 IP这个 localhost 就指向了错误的主机。另外 MySQL 默认bind-address127.0.0.1只允许本机连接即使你把 URL 改成了数据库服务器的 IPMySQL 自己也会拒绝远程连接。解决把数据库连接信息从代码里抽出来放到db.properties配置文件里# src 下的 db.properties jdbc.urljdbc:mysql://你的数据库服务器IP:3306/bookstore?characterEncodingutf8useSSLfalse jdbc.userroot jdbc.password你的密码DBUtil里用Properties.load读取这个文件代码里不再出现任何写死的连接信息。这个改动大概二十分钟但直接决定你这套系统能不能从“自己电脑能跑”变成“换环境也能跑”。如果数据库和 Tomcat 部署在同一台服务器URL 里建议写127.0.0.1而不是localhost因为某些 JDK 版本会把localhost解析成 IPv6 的::1而 MySQL 只监听了 IPv4结果本地连接反而出错。远程访问 MySQL 还需要两步把my.cnf里bind-address改成0.0.0.0并重启 MySQL云服务器的话在安全组规则里放行 3306 端口。期末演示阶段建议数据库和项目放同一台机器用127.0.0.1最省事但要知道远程连接的正确配置长什么样这是答辩时很实际的一个追问点。6. 答辩前给网上书店系统加两个加分项操作日志与下单事务期末项目答辩评委通常不关心你功能有多少而是抓两个点这个系统能不能说清楚核心链路以及你有没有考虑过数据可靠性和可追踪性。如果时间只够做两个改进我建议加一个轻量的操作日志再把第 4 章的下单事务代码在项目里完整落地。操作日志不用引入 Log4j 这种重型框架JDK 自带的java.util.logging完全够用。在 LoginServlet 和 OrderServlet 里加几行private static final Logger log Logger.getLogger(LoginServlet.class.getName()); // 登录成功时记录哪个用户、什么时间、从哪个 IP 来的 log.info(用户【 username 】登录成功IP request.getRemoteAddr()); // 登录失败时记录 warning 级别方便排查暴力尝试 log.warning(用户【 username 】登录失败IP request.getRemoteAddr());日志级别选info记录正常操作warning记录失败场景severe记录异常堆栈。控制台能看到输出IDEA 的运行日志里也能回溯。不要小看这几行老师问“你的系统怎么追踪用户行为”时你不能只说“数据库里有个用户表”而是能演示一段日志出来。下单事务这个点在前面第 4 章已经拆开讲了要落地成代码的话注意补上 finally 里的资源关闭这是很多写完事务但没写干净的地方。连接、语句、结果集全部要关闭并且要把setAutoCommit(true)恢复否则连接池复用时事务状态会串。我见过不少学生事务写对了但没关资源数据库连接池被耗尽系统跑几分钟就假死这个坑比不写事务还隐蔽。我带了几年课程设计最深的体会是期末大作业的价值不在“做了多少个功能”而在“每个功能背后的技术点能不能自己讲清楚”。加日志和事务这两个改进成本不到一小时但能让你的系统从“能跑了”变成“经得起问”。这也是我希望你在做完这份网上书店大作业后真正带走的东西。希望帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

基于YOLOv8的交通信号灯识别与通行规则判断源码深度解析 2026/9/28 16:25:49

基于YOLOv8的交通信号灯识别与通行规则判断源码深度解析

简介:Python基于YOLOv8的路口交通信号灯通行规则识别模型及算法源码,源自个人毕业设计,答辩评审成绩达98分,代码已完成调试并确保可运行。资源面向计算机、通信、人工智能、自动化等相关专业的学生、教师与从业者,既可…

阅读更多 →
智能体编排运行时ax:基于Kubernetes的Agent任务调度与检查点实践 2026/9/28 16:25:49

智能体编排运行时ax:基于Kubernetes的Agent任务调度与检查点实践

1. 从"ax"这个标题说起:一个被低估的运行时缩写第一次看到"ax"这个标题,大多数人会一头雾水。它太短了,短到像是随手敲的两个字母。但结合热搜词里的agentic、orchestration、runtime、Kubernetes这几个关键词&#xff0…

阅读更多 →
hindsight与dify结合实战:构建AI工作流的事后反思与持续进化机制 2026/9/28 16:25:49

hindsight与dify结合实战:构建AI工作流的事后反思与持续进化机制

1. 从“事后诸葛亮”到系统能力:hindsight 到底在解决什么问题“hindsight”这个词本身的意思就是“事后聪明”——事情发生完了,回头看,一切都清清楚楚。但在软件工程和 AI 应用开发领域,这个词正在被赋予一层全新的含义&#xf…

阅读更多 →
PyTorch实现Fashion-MNIST分类:CNN模型训练与调参全流程 2026/9/28 16:25:49

PyTorch实现Fashion-MNIST分类:CNN模型训练与调参全流程

简介:面向机器学习和神经网络入门者,围绕 Fashion-MNIST 时装图像分类任务,提供可直接运行的 Python 实现与配套说明文档。该数据集包含 T 恤、连衣裙、运动鞋等 10 个类别的 28x28 灰度衣物图片,其中每类有 6000 张训练图和 1000…

阅读更多 →
基于YOLOv8的车间安全穿戴检测实战:从数据集构建到部署避坑 2026/9/28 16:25:49

基于YOLOv8的车间安全穿戴检测实战:从数据集构建到部署避坑

简介:一套面向工业安全场景的目标检测数据集,聚焦车间工人、安全帽与安全背心的识别,适用于YOLO系列、Faster Rcnn、SSD等主流深度学习模型训练。数据集包含3465张图片,标注person、helmet、vest三个类别,图片与txt标签…

阅读更多 →
Univer 表格引擎实战:插件架构、Canvas 渲染与协同编辑接入指南 2026/9/28 16:25:42

Univer 表格引擎实战:插件架构、Canvas 渲染与协同编辑接入指南

1. 从“univer”这个名字说起:它到底在解决什么问题第一次看到“univer”这个词,很多人会以为是某个大学项目的缩写,或者某个开源社区的自造词。实际上,在表格与文档协同这个技术圈子里,univer 指的是一套开源的电子表…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉