ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

JSP网上玩具销售系统毕设实战:从选题到部署答辩全指南

JSP网上玩具销售系统毕设实战:从选题到部署答辩全指南 简介这是一套面向高校计算机相关专业毕业设计的完整项目源码包主题为基于JSP的网上玩具销售系统适合正在准备毕设、课程设计或想练手Java Web开发的学生与初学者。项目以JavaServer Pages为核心结合Servlet、JavaBeans实现服务端逻辑涵盖商品浏览、分类导航、购物车、用户中心与订单管理等典型电商模块并配套数据库脚本与项目报告文档。压缩包共1059个文件约23.2MB包含76个jsp页面、98个html、29个css及大量gif、jpg、png等前端素材另有js脚本、java源码、class文件、jar依赖、sql脚本与doc报告结构完整便于二次开发与学习参考。目前已有390人学习下载。读者可据此掌握JSP动态页面与数据库交互的完整流程理解导航栏设计、session与cookie管理等关键点并借助现成源码快速搭建可运行环境为答辩与后续扩展提供扎实基础。1. 网上玩具销售系统用 JSP 做毕设选题能过、代码能跑、答辩能讲清每年到毕设选题季后台被问得最多的就是「基于 JSP 的网上玩具销售系统到底还能不能做」。先说结论能做而且对计算机毕业设计里偏 Java Web 方向的同学来说它是一个性价比很高的题目——业务闭环完整浏览、下单、支付模拟、后台管理技术栈经典JSP Servlet JDBC MySQL资料多、坑也透明。它解决的核心问题是让你在有限时间里交出一个「能演示、能讲清、能改」的完整 Web 系统而不是卡在环境配置上耗掉两周。这篇文章面向三类人一是刚拿到这个选题、还没动手的应届生二是 JSP 入门阶段、分不清 JSP 和 Servlet 分工的同学三是想把传统 JSP 项目打包成 war 部署到 Tomcat 上跑通的人。我会按「先立住原理 → 再动手复现 → 最后讲坑」的顺序把表结构、核心代码、参数配置和排错路径都摊开讲。你照着做本地能跑起来一个可演示的版本你带着边界意识做答辩时被追问「为什么不用 Spring Boot」也能答得上来。2. 先想清楚JSP 在这个系统里到底负责什么很多人做这个题目的第一个翻车点是把 JSP 当成了「万能胶」——页面、逻辑、数据库全塞进 .jsp 文件里。这样写出来的东西能跑但答辩老师翻两页代码就会问「你的 MVC 在哪」。所以动手前得先把 JSP 在架构里的位置钉死。2.1 JSP、Servlet、JavaBean 的三层分工传统 JSP 模型Model 1是「JSP 里写 Java 代码」适合极小的页面而网上玩具销售系统这种有登录、购物车、订单、后台管理的项目必须用 Model 2也就是 MVCServlet 当控制器Controller接收表单、调用业务方法、决定跳哪个页面。比如/login、/cart/add、/order/submit都是 Servlet。JavaBean / DAO 当模型Model封装Toy、User、Order这些实体DAO 层负责 JDBC 增删改查。JSP 当视图View只负责把 Servlet 放进 request 的数据渲染成 HTML尽量不写业务逻辑。为什么这么分因为玩具销售系统的核心复杂度在「购物车状态」和「订单事务」上。购物车要跨多个页面保持订单提交要保证「扣库存 生成订单 清空购物车」要么全成功要么全回滚。这些逻辑放在 Servlet Service 层才好维护塞进 JSP 里迟早失控。提示答辩时如果被问「JSP 和 Servlet 区别」别背概念直接说「Servlet 处理请求和业务跳转JSP 只做展示两者通过 request/session 传数据」再指一段你的代码比背书管用。2.2 数据库表怎么设计才够用又不臃肿玩具销售系统的表不用多六张就能撑起完整闭环。下面是我一般会用的最小可用结构字段类型按 MySQL 8 写表名作用关键字段user用户含管理员id, username, password, role, phonetoy玩具商品id, name, price, stock, category_id, img, detailcategory分类id, namecart购物车项id, user_id, toy_id, numorders订单主表id, user_id, total, status, create_timeorder_item订单明细id, order_id, toy_id, num, price几个设计要点值得说清楚。第一user表用role字段区分普通用户和管理员0/1比建两张表省事登录后按 role 跳不同后台。第二cart表用user_id toy_id做唯一约束避免同一商品重复插行加购时用INSERT ... ON DUPLICATE KEY UPDATE num num 1。第三order_item里的price要单独存一份下单时的价格不能只靠toy.price关联——玩具改价后历史订单金额不能跟着变这是很多同学答辩被问到的点。CREATE TABLE toy ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(100) NOT NULL, price DECIMAL(10,2) NOT NULL, stock INT NOT NULL DEFAULT 0, category_id INT, img VARCHAR(255), detail TEXT ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE cart ( id INT PRIMARY KEY AUTO_INCREMENT, user_id INT NOT NULL, toy_id INT NOT NULL, num INT NOT NULL DEFAULT 1, UNIQUE KEY uk_user_toy (user_id,toy_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;DECIMAL(10,2)存价格而不是FLOAT是因为浮点数算金额会出现0.10.20.30000000000000004这种玄学问题订单总额对不上账。utf8mb4是为了支持商品名里的 emoji 和生僻字用utf8有时会插入失败。2.3 为什么不用 Spring Boot把选型理由讲成优势现在网上到处是「基于 Spring Boot 的 XX 系统」答辩老师很可能问你为什么还用 JSP。这不是劣势关键看你怎么讲。我的建议是三点一是 JSP 是 Java Web 的教学基础能体现你对 Servlet 生命周期、request/session 作用域、MVC 分层的理解这些在 Spring Boot 里被框架封装掉了二是传统 JSP 项目打包成 war 部署到 Tomcat整个链路你都能手动控制出问题能定位到具体一层三是这个题目业务量小引入 Spring Boot 属于杀鸡用牛刀反而增加配置复杂度。把「我选 JSP 是因为它让我把 Web 请求的完整链路走了一遍」讲出来比硬吹技术新更让老师认可。当然如果你时间充裕可以在 JSP 版本跑通后把 DAO 层换成 MyBatis 作为加分项但别一上来就上框架容易卡在环境上。3. 从零跑通IDEA 建 JSP 项目到第一个页面原理讲完进入动手环节。这一章的目标是在 IDEA 里新建一个能跑的 JSP 项目配好 Tomcat访问到一个真实页面。热词里「idea新建jsp项目」被搜得很多说明这一步确实卡人我把每一步和参数都写清楚。3.1 IDEA 新建项目与目录结构打开 IDEAFile → New → Project左侧选Java Enterprise新版叫Jakarta EE勾选Web Application。注意 JDK 选 8 或 11Tomcat 选 8.5 或 9——JSP 在 Tomcat 10 之后因为 Jakarta 命名空间变更javax.servlet要改成jakarta.servlet新手很容易在这里翻车建议直接用 Tomcat 9。建好后目录长这样src/ main/ java/ - Servlet、DAO、实体类 webapp/ - JSP 页面、WEB-INF WEB-INF/ web.xml - 部署描述符 index.jspwebapp就是网站的根目录index.jsp是默认首页。WEB-INF下的文件浏览器访问不到所以web.xml放这里安全。Servlet 的映射可以写在web.xml也可以用WebServlet注解我一般用注解少写配置。3.2 配 Tomcat 与第一个可访问页面Run → Edit Configurations → → Tomcat Server → Local在Server标签页指定 Tomcat 安装目录Deployment标签页点 → Artifact选xxx:war exploded。Application context设成/toy这样访问路径就是http://localhost:8080/toy/。先写一个最小 JSP 验证环境% page contentTypetext/html;charsetUTF-8 languagejava % html headtitle玩具商城/title/head body h1网上玩具销售系统/h1 % // 演示 JSP 内置对象out 直接输出request 取参数 String user request.getParameter(user); out.println(当前时间 new java.util.Date()); if (user ! null) out.println(欢迎 user); % /body /htmlcontentTypetext/html;charsetUTF-8必须写否则中文会乱码成问号。% page %是页面指令% %是脚本片段里面能直接用request、out、session这些内置对象。启动 Tomcat浏览器打开http://localhost:8080/toy/index.jsp?usertest能看到时间和欢迎语说明环境通了。注意如果报ClassNotFoundException: javax.servlet...八成是 Tomcat 版本和 JDK 不匹配或者Project Structure → Modules里没把 Tomcat 的库加进依赖。检查Libraries里有没有Tomcat相关 jar。3.3 用 Servlet 处理登录请求环境通了写第一个真正的控制器。登录流程是JSP 表单 POST 到/loginServlet 查库校验成功把 user 放进 session 并跳首页失败回登录页带错误信息。WebServlet(/login) public class LoginServlet extends HttpServlet { private UserDao userDao new UserDao(); Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding(UTF-8); // 必须在取参数前设置 String username req.getParameter(username); String password req.getParameter(password); User user userDao.findByUsername(username); if (user ! null user.getPassword().equals(password)) { req.getSession().setAttribute(user, user); // 登录态存 session resp.sendRedirect(req.getContextPath() /index.jsp); } else { req.setAttribute(msg, 用户名或密码错误); req.getRequestDispatcher(/login.jsp).forward(req, resp); } } }req.setCharacterEncoding(UTF-8)一定要在getParameter之前调用否则 POST 中文参数乱码。sendRedirect是重定向浏览器地址会变适合登录成功后跳转forward是服务器内部转发地址不变适合带错误信息回原页面。这两个的区别是答辩高频问题记住「重定向两次请求、转发一次请求」就够了。4. 购物车与订单把核心业务逻辑写扎实登录只是入口真正体现工作量的是购物车和订单。这一章把这两个模块的实现讲透包括 session 和数据库两种购物车方案的取舍、订单事务怎么写、库存怎么扣。4.1 购物车session 存还是数据库存购物车有两种常见做法。session 方案把MapInteger, CartItem放进 session用户不登录也能加购实现简单但换浏览器就丢且服务器重启清空。数据库方案登录用户的购物车存cart表跨设备保留但未登录用户没法用。我的建议是毕设里用数据库方案因为答辩时「购物车数据持久化」是个加分点而且能顺带展示你对表关联的掌握。未登录用户点加购时先跳登录页登录后再加。核心代码public class CartDao { // 加购存在则数量1不存在则插入 public void add(int userId, int toyId) throws SQLException { String sql INSERT INTO cart(user_id, toy_id, num) VALUES(?,?,1) ON DUPLICATE KEY UPDATE num num 1; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setInt(1, userId); ps.setInt(2, toyId); ps.executeUpdate(); } } // 查询某用户购物车联表带出玩具名和价格 public ListCartItem listByUser(int userId) throws SQLException { String sql SELECT c.id, c.num, t.name, t.price, t.id AS toy_id FROM cart c JOIN toy t ON c.toy_id t.id WHERE c.user_id ?; ListCartItem list new ArrayList(); try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setInt(1, userId); try (ResultSet rs ps.executeQuery()) { while (rs.next()) { CartItem item new CartItem(); item.setId(rs.getInt(id)); item.setNum(rs.getInt(num)); item.setName(rs.getString(name)); item.setPrice(rs.getBigDecimal(price)); list.add(item); } } } return list; } }ON DUPLICATE KEY UPDATE依赖前面建的uk_user_toy唯一索引没有这个索引就不会触发更新会直接报主键冲突或插入重复行。try-with-resources保证 Connection、PreparedStatement、ResultSet 自动关闭避免连接泄漏——Tomcat 连接池被占满后整个系统卡死是新手最常见的性能翻车。4.2 订单提交一个事务里做完三件事下单是最容易出 bug 的地方。正确流程是校验库存 → 扣库存 → 写订单主表 → 写订单明细 → 清空购物车。这五步必须在一个事务里任何一步失败全部回滚否则会出现「库存扣了但订单没生成」这种对不上账的情况。public boolean submitOrder(int userId, ListCartItem items) throws SQLException { Connection conn null; try { conn DBUtil.getConnection(); conn.setAutoCommit(false); // 开启事务 BigDecimal total BigDecimal.ZERO; for (CartItem item : items) { // 扣库存带 stock num 条件防止超卖 String deduct UPDATE toy SET stock stock - ? WHERE id ? AND stock ?; try (PreparedStatement ps conn.prepareStatement(deduct)) { ps.setInt(1, item.getNum()); ps.setInt(2, item.getToyId()); ps.setInt(3, item.getNum()); if (ps.executeUpdate() 0) { conn.rollback(); return false; // 库存不足回滚 } } total total.add(item.getPrice().multiply(new BigDecimal(item.getNum()))); } // 写订单主表拿到自增 orderId int orderId; String orderSql INSERT INTO orders(user_id, total, status, create_time) VALUES(?,?,0,NOW()); try (PreparedStatement ps conn.prepareStatement(orderSql, Statement.RETURN_GENERATED_KEYS)) { ps.setInt(1, userId); ps.setBigDecimal(2, total); ps.executeUpdate(); try (ResultSet rs ps.getGeneratedKeys()) { rs.next(); orderId rs.getInt(1); } } // 写明细 清购物车 for (CartItem item : items) { try (PreparedStatement ps conn.prepareStatement( INSERT INTO order_item(order_id, toy_id, num, price) VALUES(?,?,?,?))) { ps.setInt(1, orderId); ps.setInt(2, item.getToyId()); ps.setInt(3, item.getNum()); ps.setBigDecimal(4, item.getPrice()); ps.executeUpdate(); } } try (PreparedStatement ps conn.prepareStatement(DELETE FROM cart WHERE user_id ?)) { ps.setInt(1, userId); ps.executeUpdate(); } conn.commit(); return true; } catch (SQLException e) { if (conn ! null) conn.rollback(); throw e; } finally { if (conn ! null) { conn.setAutoCommit(true); conn.close(); } } }关键点有三个。第一扣库存的 SQL 带AND stock ?靠数据库的行锁保证并发下不会超卖比先查再改安全。第二Statement.RETURN_GENERATED_KEYS拿自增主键用来写明细表的外键。第三finally里恢复autoCommit再关连接因为连接池里的连接是复用的不恢复会影响下一个请求。4.3 后台管理商品增删改查与订单状态后台是体现「系统完整度」的部分。管理员登录后role1进入admin/目录下的页面能对玩具做增删改查、查看订单并改状态0 待付款、1 已付款、2 已发货、3 已完成。商品上传图片用commons-fileupload或 Servlet 3.0 的MultipartConfig。我一般用后者少引一个依赖WebServlet(/admin/toy/add) MultipartConfig(maxFileSize 5 * 1024 * 1024) // 单文件最大 5MB public class ToyAddServlet extends HttpServlet { protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding(UTF-8); String name req.getParameter(name); BigDecimal price new BigDecimal(req.getParameter(price)); int stock Integer.parseInt(req.getParameter(stock)); Part filePart req.getPart(img); // 对应表单 enctypemultipart/form-data String fileName System.currentTimeMillis() _ filePart.getSubmittedFileName(); String saveDir getServletContext().getRealPath(/upload); filePart.write(saveDir File.separator fileName); Toy toy new Toy(name, price, stock, upload/ fileName); new ToyDao().insert(toy); resp.sendRedirect(req.getContextPath() /admin/toy/list); } }MultipartConfig的maxFileSize要设否则用户传大文件会撑爆内存。getRealPath(/upload)拿到的是部署目录下的物理路径注意 war 重新部署后上传的文件会丢生产环境要存到项目外的固定目录毕设演示阶段存这里够用。5. 打包部署与那些让人头大的坑代码写完最后一步是打包成 war 部署到 Tomcat。热词里「传统jsp项目打包war」和「nginx 支持jsp吗」都指向部署环节这一章把打包、部署和常见故障讲清楚。5.1 打包 war 与部署到 TomcatIDEA 里File → Project Structure → Artifacts → → Web Application: Archive选war勾选Build on make然后Build → Build Artifacts → Build在out/artifacts/下生成 war 包。部署有两种方式一是把 war 直接丢进 Tomcat 的webapps/目录启动 Tomcat 会自动解压二是用Manager App页面上传。命令行验证# 拷贝 war 到 webapps cp toy.war $CATALINA_HOME/webapps/ # 启动 Tomcat $CATALINA_HOME/bin/startup.sh # Linux/Mac # 或 Windows 下 %CATALINA_HOME%\bin\startup.bat # 访问 # http://localhost:8080/toy/war 包名决定访问路径toy.war解压后就是/toy。如果访问 404先看logs/catalina.out有没有部署异常再看webapps/toy/是否解压成功。注意nginx 本身不支持 JSP它只能处理静态资源。JSP 必须由 Tomcat 这类 Servlet 容器解析。常见架构是 nginx 做反向代理把.jsp和动态请求转发给 Tomcat静态图片、CSS 由 nginx 直接返回。别指望 nginx 单独跑 JSP。5.2 避坑清单五个高频故障的排查路径现象一页面中文全是乱码。原因JSP 页面、Servlet 请求、数据库连接三处编码不一致。解决JSP 顶部写contentTypetext/html;charsetUTF-8Servlet 里req.setCharacterEncoding(UTF-8)放在取参数前JDBC URL 加?useUnicodetruecharacterEncodingutf8数据库和表用utf8mb4。四处对齐才彻底。现象二ClassNotFoundException: com.mysql.cj.jdbc.Driver。原因MySQL 驱动 jar 没放进WEB-INF/lib。解决把mysql-connector-java-8.x.jar拷进webapp/WEB-INF/libIDEA 里在Project Structure → Artifacts确认它被包含进 war。注意 MySQL 8 的驱动类是com.mysql.cj.jdbc.Driver不是老的com.mysql.jdbc.Driver。现象三登录后刷新页面登录态就没了。原因session 超时或没正确取。解决检查web.xml里session-configsession-timeout是不是设得太短默认 30 分钟以及 JSP 里取用户是不是用session.getAttribute(user)而不是request。session 和 request 作用域搞混是新手高频错误。现象四下单后库存变成负数。原因扣库存没加stock num条件或者没在事务里。解决用第 4 章那段带条件的 UPDATE并确保setAutoCommit(false)和commit/rollback成对出现。并发测试可以用 JMeter 或写个多线程脚本模拟。现象五Tomcat 启动报端口被占用。原因8080 被其他程序另一个 Tomcat、某些开发工具占了。解决改conf/server.xml里的Connector port8080为 8081或杀掉占用进程。Windows 下netstat -ano | findstr 8080找 PID再taskkill /PID xxx /F。6. 让答辩加分三个能立刻用上的进阶技巧基础版本跑通后怎么让这个毕设从「能过」变成「能拿高分」分享三个我实际用过、成本低但效果明显的技巧。技巧一给关键操作加日志答辩时能演示排错。引入log4j或直接用java.util.logging在登录、下单、扣库存处打日志。答辩老师问「如果订单出问题你怎么查」你打开日志文件指给他看比空谈强得多。配置log4j.properties输出到文件格式带上时间、线程、类名。技巧二用过滤器统一处理编码和登录拦截。写一个CharacterEncodingFilter统一设 UTF-8再写一个LoginFilter拦截/admin/*和/order/*未登录直接跳登录页。这样业务代码里不用重复写编码设置也避免漏拦截。过滤器用WebFilter(/*)注册doFilter里判断 session 有没有 user。WebFilter(/*) public class EncodingFilter implements Filter { public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { req.setCharacterEncoding(UTF-8); resp.setContentType(text/html;charsetUTF-8); chain.doFilter(req, resp); // 放行交给下一个过滤器或 Servlet } }chain.doFilter是放行不调用它请求就断在这了。过滤器顺序按web.xml里的声明或注解的默认顺序编码过滤器要放最前。技巧三准备一份「技术选型对比表」放进论文。把 JSP/Servlet 和 Spring Boot、session 购物车和数据库购物车、Statement和PreparedStatement的对比列成表说明你为什么这么选。答辩时老师最爱问「为什么」有对比表你就有据可依。比如PreparedStatement能防 SQL 注入、预编译效率高这就是标准答案。最后说个我自己的习惯每次改完代码先本地 Tomcat 跑一遍完整流程——注册、登录、加购、下单、后台改状态五步走完再提交。血泪经验是很多 bug 只在「下单后清空购物车」这种跨模块操作里暴露单测一个页面根本发现不了。毕设拼的不是技术多新而是流程多完整、细节多扎实。把这条链路走顺你的网上玩具销售系统就稳了。希望帮到你。本文还有配套的精品资源点击获取
返回列表