JSP蛋糕商城系统实战:JavaWeb毕设从开发到部署全流程

发布时间:2026/9/28 13:45:57
JSP蛋糕商城系统实战:JavaWeb毕设从开发到部署全流程 简介基于JSP的蛋糕商城系统是一份完整的JavaWeb实战项目包含源码与SQL数据库面向需要电商系统开发参考的学习者。系统覆盖商品管理、分类管理、订单管理等核心模块管理员可对商品进行添加、修改、下架维护商品分类并处理订单发货前端基于JSPServlet实现页面与业务逻辑后端以SQL脚本完成数据持久化。资源压缩包共408个文件约21.93MB包含55个Java源文件、58个JSP页面、110个Class编译文件以及SQL数据库脚本、XML配置、JAR依赖库和全套前端图片素材jpg/gif/png等目录结构清晰便于直接导入开发环境运行与二次开发。已有3238人学习/浏览适合作为课程设计、毕业设计或就业项目参考通过阅读改造可掌握电商系统从页面展示、请求处理到数据库操作的完整流程并理解前后端集成与权限管理思路。1. JSP蛋糕商城系统到底是什么一次JavaWeb毕设的完整闭环如果你在毕业设计或JavaWeb课程设计里搜到过这个标题大概率是下载了一个打好的压缩包一堆JSP页面、几个Servlet类、lib目录下的jar包外加一个xxx.sql数据库脚本。这套基于JSP的蛋糕商城系统本质是JavaWeb最经典的三层结构——JSP负责界面Servlet处理请求JDBC读写MySQLTomcat负责跑起来。它不算新潮但胜在链路完整用户注册登录、蛋糕分类展示、购物车、模拟下单、后台订单管理都在一个工程里。适合三类人想做毕设但不想从零写代码的想拿一个完整JavaWeb项目练手、把Servlet和JSP彻底弄懂的以及手里已经有源码但跑不起来、想照着改造成自己作品的。这篇会按我实际跑通这类项目的顺序来写把版本搭配、数据库导入、核心链路和翻车点一次讲清。2. 把蛋糕商城跑起来JDK、Tomcat、IDEA与MySQL的版本搭配和最小配置2.1 为什么这类JSP项目必须锁版本JDK8Tomcat8.5MySQL5.7传统JSP项目的共同特点是老web.xml可能是Servlet 2.5或3.0页面里用的是JSTL 1.2依赖全是jar包直接塞在WEB-INF/lib里。这种组合最怕的不是功能复杂而是版本漂移。常见做法是把版本锁死在下面这组跑通成功率最高组件推荐版本理由JDK1.8老项目编译目标基本是1.8高版本JDK会遇到javax包缺失或JSP编译告警Tomcat8.5.x / 9.0.xServlet 3.1规范兼容老web.xml自带Jasper引擎MySQL5.7驱动和认证方式最稳不存在8.0的caching_sha2_password麻烦mysql-connector-java5.1.47对应MySQL 5.7驱动类名是com.mysql.jdbc.DriverIDEA2021-2023新版仍保留对JSP/Artifacts的完整支持我一般在解压项目后第一件事不是开IDEA而是先看两处WEB-INF/lib里有没有mysql驱动jarsrc下有没有db.properties或DBUtil.java。能看到这两个后面基本就是配置问题如果lib是空的那要先补jar包否则后面全是ClassNotFoundException。这里要注意一个跟“新版”直觉相反的点越新的运行环境越容易让老项目翻车。Tomcat 10把javax.servlet改成了jakarta.servlet老代码直接编译不过MySQL 8的驱动类名是com.mysql.cj.jdbc.Driver和老的配置文件对不上。所以别用最新版用上面的组合半小时就能跑起来。2.2 用IDEA导入JSP项目Facets、Artifacts与Tomcat运行配置拿到源码后不要直接Open整个文件夹建议用IDEA的New - Project from Existing Sources导入选Maven就选pom.xml没有pom就选普通目录。导入后关键是配好项目的Web结构否则JSP页面会404或者根本启动不了。先检查Project Structure快捷键CtrlAltShiftS里的四个位置Project SDK选JDK 1.8Language Level选8Modules - 当前模块确认有没有Web Facet没有就点添加Web把Web Resource Directory指向web或WebContent目录Libraries里确认mysql驱动、jstl、servlet-api等jar已经在列表里没有就从lib目录手动添加Artifacts选Web Application: Exploded确保Output Layout里有WEB-INF/lib而且lib下不能只有空目录配好之后在Tomcat运行配置里把Artifact加到Deployment选项卡Application context填项目名比如/cake。然后启动IDEA会编译所有JSP页面控制台出现Artifact is deployed successfully就说明Servlet上下文已经挂上了。先给一个典型的web.xml片段老项目长这样web-app xmlnshttp://xmlns.jcp.org/xml/ns/javaee xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd version3.1 display-nameCakeShop/display-name servlet servlet-nameCartServlet/servlet-name servlet-classcom.cake.servlet.CartServlet/servlet-class /servlet servlet-mapping servlet-nameCartServlet/servlet-name url-pattern/cart/url-pattern /servlet-mapping welcome-file-list welcome-fileindex.jsp/welcome-file /welcome-file-list /web-app这段配置的逻辑是把/cart这个URL交给CartServlet处理用户访问http://localhost:8080/蛋糕项目名/cart时就会进入购物车逻辑welcome-file-list指定了项目根路径默认打开index.jsp。如果你拿到手的项目里Servlet类是注解形式WebServlet那web.xml里就不会有Servlet映射这是两种写法的区别别重复配置否则启动报Servlet mapping冲突。2.3 JDBC连接MySQL驱动URL、账号密码与连接池参数传统JSP项目几乎都用一份db.properties或直接在DBUtil.java里写死连接信息。我建议保留properties文件的方式改起来方便。jdbc.drivercom.mysql.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/cake_db?useUnicodetruecharacterEncodingutf8useSSLfalse jdbc.usernameroot jdbc.password123456解释一下这几个参数为什么不能省characterEncodingutf8不写的话JSP页面往数据库写中文商品名时大概率变成问号useSSLfalseMySQL 5.7本地连不需要SSL不关会有告警日志不影响运行但看着烦useUnicodetrue和characterEncoding配套保证传输过程编码一致对应DBUtil的核心代码package com.cake.util; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; public class DBUtil { private static final String DRIVER com.mysql.jdbc.Driver; private static final String URL jdbc:mysql://localhost:3306/cake_db?useUnicodetruecharacterEncodingutf8useSSLfalse; private static final String USER root; private static final String PASSWORD 123456; static { try { Class.forName(DRIVER); } catch (ClassNotFoundException e) { throw new RuntimeException(mysql驱动加载失败, e); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(URL, USER, PASSWORD); } public static void close(Connection conn, Statement stmt, ResultSet rs) { try { if (rs ! null) rs.close(); } catch (SQLException ignored) {} try { if (stmt ! null) stmt.close(); } catch (SQLException ignored) {} try { if (conn ! null) conn.close(); } catch (SQLException ignored) {} } }这段代码的逻辑静态块里用Class.forName加载驱动确保DriverManager注册成功getConnection每调一次就创建一条物理连接这是老项目最简单但也是性能最差的方式。如果源码里已经是这种方式先不要改成连接池跑通为先。等全部功能验证过了再考虑换成Druid或C3P0池子。参数层面要注意的是如果密码里有或#这类特殊字符URL里会解析错要么改密码要么用Properties类从文件读取如果源码里密码是明文root连上数据库后第一件事就是把密码改掉免得最后交源码时被人看到真实密码。3. 导入SQL数据库并读懂表结构从建库到验证商品与订单数据的五步3.1 先建库再导表source命令与字符集的选择这个环节最常见的翻车是把.sql文件直接拖进Navicat的查询窗口执行结果外键报错、表顺序错乱。正确做法是先建库、设置字符集、再指定库导入。# 登录MySQL mysql -u root -p # 创建数据库统一utf8mb4 CREATE DATABASE IF NOT EXISTS cake_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; # 使用该库 USE cake_db; # 导入sql脚本注意路径别带中文 SOURCE /Users/yourname/Downloads/cake.sql;COLLATE utf8mb4_general_ci是老项目最常见的排序规则比较字符串时不区分大小写对登录校验友好如果源码里SQL脚本本身带CREATE DATABASE语句就不要重复执行第一步直接USE你想要的库名再SOURCE。这里有个Windows上的坑SOURCE路径不能有空格和中文建议先把sql文件拷到C:\cake.sql这种纯英文路径再执行。导入完成后敲SHOW TABLES;如果能看到表清单数据库导入这步就算过了。3.2 蛋糕商城的典型表结构用户、商品、分类、订单与明细一个完整的蛋糕商城数据库至少包含这五类核心表表名作用核心字段说明user商城用户id, username, password, phone, address密码一般是MD5字符串长度32category蛋糕分类id, name, sort如生日蛋糕、慕斯、小甜品goods蛋糕商品id, category_id, name, price, stock, image, description价格用decimal(10,2)别用floatorders订单主表id, user_id, order_no, total_price, status, create_timestatus常见0待支付/1已支付/2已发货/3已完成order_item订单明细id, order_id, goods_id, num, price下单时快照商品价格网上流传的SQL脚本里表名可能是cake、product或goods字段名也可能从count变成stock这不影响使用关键是实体类、DAO里的SQL、数据库字段名三者必须一致。你可以用下面这条SQL直接查数据库里实际有哪些表再跟源码里的实体类对一遍SELECT TABLE_NAME, TABLE_COMMENT FROM information_schema.TABLES WHERE TABLE_SCHEMA cake_db;对照的时候重点看goods表有没有stock库存字段CREATE TABLE goods ( id int(11) NOT NULL AUTO_INCREMENT, category_id int(11) DEFAULT NULL, name varchar(100) NOT NULL, price decimal(10,2) NOT NULL, stock int(11) NOT NULL DEFAULT 0, image varchar(255) DEFAULT NULL, description text, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;这段DDL里有两个容易踩的点price用decimal而不是float因为浮点算总价会出现28.999999这种结果stock设了DEFAULT 0报名时如果源码里下单逻辑没有校验库存库存为0的商品依然能下单后面会细说。user表的密码也值得看一下CREATE TABLE user ( id int(11) NOT NULL AUTO_INCREMENT, username varchar(50) NOT NULL, password varchar(64) NOT NULL, phone varchar(11) DEFAULT NULL, address varchar(255) DEFAULT NULL, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;password长度64说明存的是MD5摘要不是明文。如果你看到的是varchar(32)那基本就是MD5(password)的结果看到varchar(64)则可能是usernamepassword加盐后再MD5。登录源码里会有对应的加密方法别自己去重置密码直接用现成的注册页面造一个新用户最省事。3.3 导入后立刻执行的验证SQL三分钟确认库是完整的很多下载的sql脚本只导了表结构里面一条业务数据都没有导致登录页面永远登录不进去。导入后立刻跑这几条SQL验证-- 商品和分类是否有关联数据 SELECT c.name AS 分类, COUNT(g.id) AS 商品数 FROM category c LEFT JOIN goods g ON c.id g.category_id GROUP BY c.id; -- 有没有现成的用户密码字段是否为空 SELECT id, username, LENGTH(password) AS pwd_len FROM user LIMIT 5; -- 订单表结构是否完整订单号不能为空 SELECT COUNT(*) AS 订单总数, COUNT(DISTINCT order_no) AS 订单号种类 FROM orders;这三条SQL分别验证三件事首页分类和商品能不能正常联动展示、用户表里有没有可登录的账号、订单主键和订单号是否完善。如果商品数列全是0那就麻烦一点得找有没有goods.sql或data.sql这样的独立数据脚本如果user表一条记录都没有直接用商城前台的注册功能注册一个别手动往表里插数据因为密码加密方式可能跟你手动写的不一致。4. 加入购物车到生成订单JSPServletJDBC核心链路与war打包部署4.1 JSP页面怎么展示商品列表JSTL与EL表达式的一次说清老项目的首页index.jsp几乎都是这个套路顶部导航、分类列表、商品卡片网格。商品循环展示靠的是JSTL的c:forEach数据来源是ServletsetAttribute进request域的值。% page contentTypetext/html;charsetUTF-8 languagejava % % taglib prefixc urihttp://java.sun.com/jsp/jstl/core % html headtitle蛋糕商城/title/head body div classgoods-grid c:forEach items${goodsList} varg div classgoods-card img src${g.image} alt${g.name} width200 height200/ h3${g.name}/h3 p classprice${g.price}/p p库存${g.stock}/p a hrefcart?actionaddgoodsId${g.id}加入购物车/a /div /c:forEach /div /body /html这段页面代码的逻辑${goodsList}是Servlet放进request里的Listvarg相当于Java的for (Goods g : goodsList)循环变量${g.name}调用的是Goods实体类的getName()方法。参数层面要注意如果页面只显示${g.name}却显示不出图片排查顺序是——先看浏览器开发者工具里图片URL是不是404再查image字段存的是相对路径还是http://开头很多教程里的图片路径是upload/xxx.jpg但Tomcat默认不会把本地磁盘路径映射到upload目录需要额外配置虚拟路径映射这一步也是新手最容易卡住的地方。当初我跑这类项目时图片全挂就是没配虚拟路径后来在server.xml的Host标签里加了Context映射才解决。4.2 购物车存Session还是数据库CartServlet的核心写法购物车是这类系统的核心枢纽。常见做法有两种小项目存在Session里重启就丢讲究一点的存数据库。课程设计级别的源码大多是Session方案实现快、代码直观。package com.cake.servlet; import com.cake.entity.Goods; import com.cake.dao.GoodsDao; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.io.IOException; import java.util.HashMap; import java.util.Map; WebServlet(/cart) public class CartServlet extends HttpServlet { private GoodsDao goodsDao new GoodsDao(); protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String action request.getParameter(action); HttpSession session request.getSession(); if (add.equals(action)) { int goodsId Integer.parseInt(request.getParameter(goodsId)); // 从session取购物车Mapkey是商品idvalue是数量 MapInteger, Integer cart (MapInteger, Integer) session.getAttribute(cart); if (cart null) { cart new HashMapInteger, Integer(); } // 加购同款商品数量1 Integer count cart.get(goodsId); if (count null) { cart.put(goodsId, 1); } else { cart.put(goodsId, count 1); } session.setAttribute(cart, cart); response.sendRedirect(cart?actionlist); } else if (list.equals(action)) { request.getRequestDispatcher(cart.jsp).forward(request, response); } } }这段代码的逻辑购物车用Map商品id, 数量存在Session里add动作做数量累加list动作跳转到购物车页面。这里最容易被忽略的是HttpSession的作用域问题——同一个浏览器会话里用户登录前加购和登录后加购Session是同一个但只要关闭浏览器购物车就清空了这是Session方案的根本局限。参数说明goodsId从URL的query string取必须保证是纯数字否则Integer.parseInt直接抛异常。实际项目里至少要做一层try-catch我见过不下三次因为商品id传空导致整个页面500。4.3 下单时的事务与库存扣减先把Connection事务写对购物车结算后就是生成订单这块是整篇代码里唯一涉及数据库事务的地方。老项目很多是三条SQL分开执行没有事务结果就是订单主表插入成功、明细表插入失败、库存扣减了但查不到订单。血泪经验告诉我下单必须用事务包起来。Connection conn null; try { conn DBUtil.getConnection(); conn.setAutoCommit(false); // 开启事务 // 1. 插入订单主表 String insertOrder INSERT INTO orders (user_id, order_no, total_price, status, create_time) VALUES (?, ?, ?, 0, NOW()); PreparedStatement ps1 conn.prepareStatement(insertOrder, Statement.RETURN_GENERATED_KEYS); ps1.setInt(1, userId); ps1.setString(2, generateOrderNo()); ps1.setBigDecimal(3, totalPrice); ps1.executeUpdate(); // 2. 拿到自增主键 ResultSet rs ps1.getGeneratedKeys(); int orderId 0; if (rs.next()) { orderId rs.getInt(1); } // 3. 插入订单明细 String insertItem INSERT INTO order_item (order_id, goods_id, num, price) VALUES (?, ?, ?, ?); PreparedStatement ps2 conn.prepareStatement(insertItem); for (CartItem item : cartItems) { ps2.setInt(1, orderId); ps2.setInt(2, item.getGoodsId()); ps2.setInt(3, item.getNum()); ps2.setBigDecimal(4, item.getPrice()); ps2.addBatch(); } ps2.executeBatch(); conn.commit(); // 全部成功才提交 } catch (Exception e) { if (conn ! null) { conn.rollback(); // 任何一步失败都回滚 } throw new RuntimeException(下单失败, e); } finally { if (conn ! null) conn.close(); }这段代码的逻辑setAutoCommit(false)之后插入主表、插入明细、扣库存这三件事变成一个原子操作中间任何一条SQL抛异常rollback都会把之前没提交的数据撤销。关键参数是Statement.RETURN_GENERATED_KEYS它让MySQL返回自增主键这样订单明细才能关联到订单主表。如果你用SELECT LAST_INSERT_ID()那必须在事务内的同一条连接上执行否则拿到的是别人插入的id。如果源码里没有事务优先补上别嫌麻烦。线上商城出一次数据不一致比加这十行代码痛苦一百倍。4.4 传统JSP项目打包warIDEA导出与Tomcat的webapps部署平时开发用IDEA里的Tomcat配置就行但最终交作业是交war包答辩时考官也可能让你部署到独立Tomcat。在IDEA里点Build - Build Artifacts - 你的war包 - Rebuild产物在out/artifacts/目录下。拿到war包部署有两种路径# 方式一直接把war拷到Tomcat的webapps目录 cp cake.war /opt/tomcat9/webapps/ # 启动Tomcat自动解压 /opt/tomcat9/bin/startup.sh # 方式二服务器上手动指定war路径启动适用于多应用部署 /opt/tomcat9/bin/catalina.sh startwar包部署的逻辑Tomcat启动时扫描webapps目录发现war包自动解压成同名目录所以访问路径就是http://服务器IP:8080/cake/。如果你的项目名变成cake但数据库连接还是localhost:3306说明DBUtil里写的是本地地址部署到服务器后要改成服务器的内网IP或保持localhost如果Tomcat和MySQL在同一台机器。顺带回答一个老生常谈的问题nginx支持jsp吗nginx本身不解析JSPJSP的编译和运行是Tomcat的Jasper引擎干的活。生产环境常见做法是nginx做反向代理监听80端口把所有动态请求转发给Tomcat的8080端口静态资源由nginx自己扛。课程设计阶段不需要nginx直接8080端口访问就够了别在这上面浪费时间。5. JSP蛋糕商城最常见的五个坑从启动报错到中文乱码的排查记录5.1 MySQL 8驱动报错Public Key Retrieval is not allowed现象用MySQL 8.0的库连接时报Public Key Retrieval is not allowed项目直接启动不了。原因MySQL 8.0默认的认证插件是caching_sha2_password老驱动或JDBC URL默认不允许从服务器取公钥来加密密码传输。解决两种方式任选。# 方式一JDBC URL加参数允许取公钥最简单 jdbc.urljdbc:mysql://localhost:3306/cake_db?useSSLfalseallowPublicKeyRetrievaltrue # 方式二把用户密码改成mysql_native_password治本 ALTER USER rootlocalhost IDENTIFIED WITH mysql_native_password BY 123456;我建议做毕设用方式二把root密码改回native方式这能同时解决高版本MySQL和新驱动之间一堆杂乱问题。5.2 导入的SQL文件中文全部乱码现象MySQL里SELECT出来中文名变成????JSP页面显示也是问号。原因SQL文件本身的编码是GBK或ANSI但建库时指定了utf8mb4两边对不上。解决用文本编辑器比如VS Code或Notepad打开sql文件把编码另存为UTF-8再重新导入。如果已经导入完了就用ALTER TABLE goods CONVERT TO CHARACTER SET utf8mb4;把表转换一遍。最保险的流程是先改文件编码再删库重建再导入三步走完不再有乱码。顺带说一句网上很多sql脚本里的注释还是繁体中文如果直接执行会因为字符集问题报错把开头所有注释行删掉再导。5.3 8080端口被占用Tomcat启动秒退现象启动Tomcat日志刷到一半说Port 8080 is already in use然后进程消失。原因本机可能有其他Java进程、或者之前的Tomcat没停干净占了8080。解决先找到占用的进程再杀掉。Windows下用netstat -ano | findstr 8080拿到PID然后在任务管理器里结束该PIDMac/Linux用lsof -i:8080拿到进程号再kill。如果嫌麻烦直接把Tomcat的server.xml里8080改成8081改两处HTTP和AJP端口重启即可。5.4 登录提示用户名或密码错误但表里明明有数据现象注册都成功了登录就是进不去也不报500错误。原因大概率是密码加密方式对不上注册和登录用了不同的算法或者数据库里的password字段被手动改过。解决把注册页面的源码和登录页面的校验代码放在一起对比。常见的是MD5(password)和MD5(username password)两种差了哪边你去拼一下就能对上了。如果实在找不出原因就用注册功能重新注册一个账号然后再登录这是最快的后悔药。5.5 IDEA运行配置里没有Artifact选项现象下载的是没有pom.xml的传统项目在IDEA的Run Configuration里找不到Deployment - Artifact。原因IDEA没有识别出Web模块Facets和Artifacts都没自动生成。解决手动补配。在Project Structure里给模块添加Web依赖源路径再手动新建一个Web Application: Exploded的Artifact把编译后的classes和lib库加进Output Layout。这一步做完运行配置里才会出现Artifact。这是这类传统JSP项目在IDEA里最常见的疑难杂症也是IDEA无法像Eclipse那样自动识别WebContent目录导致的。6. 把蛋糕商城改成自己的换皮、加功能和答辩前验证清单6.1 二十分钟换皮店名、配色和CSS变量的快速替换交作业前最怕的就是跟网上其他人的演示截图长得一模一样。别整体重写只动三层壳首页标题、CSS配色、导航栏文案。修改JSP页面前先看一眼HTML头部link relstylesheet hrefcss/style.css把style.css打开找颜色值和品牌名用CSS变量统一替换:root { --main-color: #e07a9b; /* 原来可能是蓝色系 */ --header-bg: #fff5f7; } .goods-card .price { color: var(--main-color); }改完后全站的蛋糕店氛围就有了这是成本最低的差异化。接着把index.jsp里的店名、title、导航菜单里的文案全部替换成自己的注意同时改header.jsp如果每个页面都拷贝了一遍导航那全文替换店名即可别漏了footer。6.2 答辩前必须走通的六条验证用例改完之后按下面这张表从头到尾手点一遍别只在浏览器里看首页功能操作步骤预期结果注册首页点注册填手机号密码成功后自动登录或跳转登录页登录用刚注册的账号登录首页右上角显示用户名商品浏览点一个分类再点商品详情图片、价格、库存正常显示加入购物车商品详情页点加入购物车购物车数量1刷新不丢下单购物车点结算填收货地址生成订单库存减少后台管理管理员登录看到新订单订单状态可改成已发货6.3 交源码前删掉这三样东西第一DBUtil.java里明文数据库密码改成读取配置文件或环境变量至少造一个跟自己个人账号完全不同的假密码。第二项目里的out和target编译目录连同.idea和*.iml一起删掉或排除掉。第三Tomcat日志目录不要装进压缩包。这次做改造时我是直接从别人源码包的admin后台开始动手的结果发现后台页面里留了原始开发者的一堆测试按钮和数据光清理就花了半小时。后来学乖了凡是下载的模板项目一律先全局搜索开发者名、默认密码、测试账号全部替换干净再开始加功能。希望这些经验能帮你在做JSP蛋糕商城这条路上少走点弯路早点交出一份真正属于自己的作品。本文还有配套的精品资源点击获取