☰
JavaWeb鲜花销售系统:Servlet+JSP+MySQL实战项目解析
2026/10/7 13:18:04 网站建设 项目流程

简介:这是一套面向计算机专业本科生的JavaWeb课程设计与期末大作业实战资源,聚焦鲜花销售业务场景,提供完整可运行的B/S架构系统源码及配套数据库,适用于正在完成大作业、需项目练手或巩固Servlet+JSP+MySQL开发流程的学习者。资源共55个文件,含31个Java核心类(覆盖Entity、DAO、Service、Servlet及Factory模式实现)、17张界面与流程图PNG(含登录页、商品管理、订单处理等模块截图)、4个JFD格式UML类图、1个SQL建库脚本(database.sql)及基础配置文件,压缩包仅6.27MB,轻量易部署。已有474人学习下载,项目经导师评审获98分高分,代码结构清晰、分层合理,包含Tools工具类、Forms表单验证、Realize接口实现及完整文档说明,可直接用于答辩演示或二次开发拓展。

1. 这不是“抄作业”,而是用 JavaWeb 搭建真实可运行的鲜花销售系统:从数据库建模到前后端联调,95分背后是 3 类典型业务闭环的扎实落地

你拿到的这个“期末大作业Javaweb的鲜花销售管理系统源码+数据库(95分以上).zip”,表面看是个学生作业包,但拆开后你会发现:它完整覆盖了 JavaWeb 工程中最常被考、也最容易翻车的三大核心闭环——用户登录鉴权与角色分离(管理员/顾客)、商品全生命周期管理(上架/下架/库存扣减/分类检索)、订单状态机驱动的交易流程(待支付→已发货→已完成→已取消)。这不是拼凑的 CRUD 堆砌,而是用 Servlet + JSP + JDBC + MySQL 实现的轻量级但逻辑自洽的电商雏形。它不依赖 Spring Boot 自动装配,所有连接池、事务控制、参数校验都手写可控;数据库设计包含 7 张表(user, flower, category, order_header, order_item, cart, address),外键约束明确,索引合理(category_id、status、create_time 均有索引)。适合两类人:一是刚学完 JDBC 和 Servlet 的新手,想拿一个能真正在 Tomcat 里跑起来、能改、能 debug、能讲清每行代码作用的练手项目;二是需要快速验证 JavaWeb 技术栈落地能力的转岗者或培训学员——它没用任何“黑匣子”框架,所有技术点都在教科书范围内,但又比课本案例多出真实业务细节(比如库存扣减时的并发安全处理、订单超时自动关闭逻辑)。别被“期末大作业”字眼迷惑,这是一份可直接用于技术面试演示、课程设计答辩、甚至微服务拆分前的单体原型。


2. 用 IDEA 运行 JavaWeb 项目配置:从 JDK 到 Tomcat,绕过 90% 的环境报错

2.1 环境准备:确认 JDK 版本、Tomcat 版本与项目兼容性

这个项目基于 Java 8 编写(pom.xml或build.xml中未声明高版本,且源码中无 Lambda 表达式以外的 Java 11+ 语法),因此必须使用JDK 8u2XX(推荐 8u291)。高于 JDK 11 会导致javax.servlet.*包找不到(Java EE API 已从 JDK 移除),低于 JDK 8u151 可能因 TLS 协议变更导致数据库连接失败。Tomcat 推荐使用Apache Tomcat 8.5.99(官方最后支持 Java 8 的 8.5.x 版本)或Tomcat 9.0.89(最低要求 JDK 8,兼容性更稳)。不要用 Tomcat 10+,其 Servlet API 已升级为 Jakarta EE 命名空间(jakarta.servlet.*),而本项目仍用javax.servlet.*,强行部署会直接报NoClassDefFoundError。

提示:检查项目根目录下是否有build.xml(Ant 构建)或pom.xml(Maven 构建)。本项目大概率是 Ant 构建(因标题强调“源码+数据库”,未提 Maven 依赖管理),所以无需mvn clean package,直接导入为普通 Java Web 项目即可。

2.2 IDEA 导入步骤:三步完成项目识别与模块配置

# 第一步:解压 zip 后,打开 IDEA → "Open" → 选择解压后的根目录(含 src、WebContent、WEB-INF 目录) # 第二步:IDEA 会弹出 "Import Project" 对话框 → 选择 "Import project from external model" → 不勾选 Maven/Gradle → 点击 OK # 第三步:右键项目根目录 → "Add Framework Support..." → 勾选 "Web Application" → Version 3.1 → 确认

关键动作在第三步:必须手动指定 Web Application Version 为3.1(对应 Servlet 3.1 规范),因为项目web.xml中<web-app>根节点声明为version="3.1"。若选错为 4.0 或 5.0,IDEA 会生成错误的web.xml模板,导致Filter和Listener注册失败。

2.3 Tomcat 配置:部署路径、上下文路径与热更新设置

在 IDEA 中配置 Tomcat Run Configuration:

  • Application server: 选择已下载解压的 Tomcat 8.5 或 9.0 目录
  • Deployment → Artifact: 点击 "+" → "Exploded" → 选择项目名_war_exploded(IDEA 自动生成)
  • Application context: 必须设为/flower(项目默认访问路径,见web.xml中 welcome-file-list 或登录页跳转逻辑)
  • On 'Update' action: 选"Update classes and resources"(非 "Redeploy")——这是 JavaWeb 热更新的关键,避免每次改 JSP 都重启 Tomcat
  • On frame deactivation: 同样选"Update classes and resources",保证切回 IDEA 时自动同步

注意:WEB-INF/web.xml中<welcome-file-list>指向login.jsp,所以浏览器访问http://localhost:8080/flower会自动跳转到登录页。若看到 404,请先检查 Application context 是否为/flower,再确认login.jsp是否在WebContent/根目录下。

2.4 数据库连接配置:修改 JDBC URL、用户名、密码并验证连通性

项目数据库配置集中在两个位置:

  • src/jdbc.properties(最常见):内容类似
    driver=com.mysql.cj.jdbc.Driver url=jdbc:mysql://localhost:3306/flower_db?useSSL=false&serverTimezone=GMT%2B8&characterEncoding=utf8 username=root password=123456
  • 或硬编码在src/utils/DBUtil.java中(较少见,但需检查)

你需要做三件事:

  1. 确认 MySQL 服务已启动:命令行执行mysql -u root -p能登录即表示服务正常
  2. 创建数据库并导入 SQL:用flower_db.sql(通常在 zip 包根目录或doc/子目录)导入
    mysql -u root -p -e "CREATE DATABASE IF NOT EXISTS flower_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;" mysql -u root -p flower_db < flower_db.sql
  3. 修改配置文件中的密码:将password=123456改为你本地 MySQL 的实际密码(如忘记,重置 root 密码:ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'your_new_pass';)

验证是否连通:启动 Tomcat 后,访问http://localhost:8080/flower/login.jsp,输入任意账号密码(如admin/123456),若跳转到main.jsp或显示“用户名或密码错误”,说明 JDBC 连接成功;若页面空白或报SQLException: Access denied,则密码或权限不对。


3. 数据库建模与 SQL 脚本解析:7 张表如何支撑鲜花销售的核心业务流

3.1 表结构设计逻辑:为什么是这 7 张表?每张表解决什么问题?

本项目数据库采用第三范式设计,但对高频查询做了适度冗余(如order_header中保留total_amount字段,避免实时 SUM 计算)。7 张表分工明确:

表名主要字段业务作用关键约束
userid, username, password, role, phone, email用户身份与权限中心role ENUM('admin','customer') NOT NULL
categoryid, name, description鲜花品类管理(玫瑰、百合、康乃馨等)name UNIQUE
flowerid, name, price, stock, category_id, image_path, status商品主数据,含库存与上下架状态stock >= 0,status TINYINT(1) DEFAULT 1 (1=上架,0=下架)
cartid, user_id, flower_id, quantity, add_time用户购物车临时存储(user_id, flower_id) UNIQUE
order_headerid, user_id, total_amount, status, create_time, update_time, address_id订单头信息,记录总金额与状态变迁status ENUM('pending','paid','shipped','completed','cancelled')
order_itemid, order_id, flower_id, quantity, price_at_order订单明细,快照下单时价格与数量外键order_id → order_header.id
addressid, user_id, receiver, phone, province/city/district, detail, is_default用户收货地址管理is_default TINYINT(1) DEFAULT 0

注意:flower表的status字段是业务开关,管理员在后台点击“下架”时只更新该字段,而非物理删除,保证历史订单仍能关联到商品名称。这是电商系统基础但易被忽略的设计点。

3.2 核心 SQL 脚本解读:三个关键脚本决定系统健壮性

解压后找到flower_db.sql(或类似命名),重点看以下三段:

① 创建数据库与表字符集

CREATE DATABASE flower_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- 必须用 utf8mb4!否则 emoji 或生僻汉字(如“龘”)存入会乱码或截断

②flower表的库存扣减事务逻辑

-- 在 OrderServlet.java 中执行的扣库存 SQL(伪代码) UPDATE flower SET stock = stock - ? WHERE id = ? AND stock >= ?; -- 参数:?1=购买数量,?2=商品ID,?3=购买数量 -- 关键:WHERE 中 `stock >= ?` 是乐观锁实现,避免超卖 -- 若影响行数为 0,说明库存不足,需回滚事务并提示用户

③ 订单状态自动更新触发器(部分版本含)

DELIMITER $$ CREATE TRIGGER update_order_status AFTER UPDATE ON order_header FOR EACH ROW BEGIN IF NEW.status = 'shipped' THEN UPDATE order_header SET update_time = NOW() WHERE id = NEW.id; END IF; END$$ DELIMITER ; -- 此触发器非必需,但体现状态变更的副作用管理思路

3.3 数据初始化:为什么init_data.sql比空库更重要?

项目通常附带init_data.sql(或写在flower_db.sql末尾),包含:

  • 默认管理员账号:INSERT INTO user (username, password, role) VALUES ('admin', 'e10adc3949ba59abbe56e057f20f883e', 'admin');(MD5 加密的123456)
  • 基础分类:INSERT INTO category (name) VALUES ('玫瑰'), ('百合'), ('康乃馨'), ('向日葵');
  • 测试鲜花:INSERT INTO flower (name, price, stock, category_id) VALUES ('红玫瑰', 28.00, 100, 1);

没有这些初始数据,登录后进后台会看到空列表,无法测试商品管理功能。务必在导入flower_db.sql后,再执行init_data.sql(或确认flower_db.sql已包含初始化 INSERT)。


4. 前后端交互关键链路:从登录校验到订单生成,手写 Servlet 如何替代 Spring MVC

4.1 登录流程:Filter 拦截 + Session 管理 + 角色跳转

整个登录不依赖框架,靠LoginServlet+LoginFilter实现:

  • LoginServlet:接收login.jsp表单 POST,调用UserDAO.login(username, password)查询数据库,密码为 MD5 加密比对

  • LoginFilter(src/filter/LoginFilter.java):配置在web.xml中拦截/admin/*和/customer/*路径

    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) { HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) resp; HttpSession session = request.getSession(false); if (session == null || session.getAttribute("user") == null) { response.sendRedirect(request.getContextPath() + "/login.jsp"); return; } chain.doFilter(req, resp); }

    关键点:getSession(false)不创建新 Session,避免未登录用户也生成 Session ID;session.getAttribute("user")存的是User对象,含role字段用于后续权限判断。

  • 角色跳转逻辑:登录成功后,LoginServlet根据user.getRole()决定重定向:

    if ("admin".equals(user.getRole())) { response.sendRedirect(request.getContextPath() + "/admin/main.jsp"); } else { response.sendRedirect(request.getContextPath() + "/customer/main.jsp"); }

4.2 购物车与订单生成:两次数据库写入与事务边界

用户点击“结算”触发OrderServlet,核心逻辑分三步:

  1. 读取购物车:CartDAO.findByUserId(userId)获取当前用户所有购物车项
  2. 校验库存并扣减:遍历每个Cart对象,执行FlowerDAO.updateStock(flowerId, quantity)(即前述带AND stock >= ?的 UPDATE)
    • 若任一商品库存不足,executeUpdate()返回 0,立即conn.rollback()并返回错误
  3. 插入订单头与明细:
    // 插入 order_header String sql1 = "INSERT INTO order_header (user_id, total_amount, status) VALUES (?, ?, 'pending')"; // 插入 order_item(循环执行) String sql2 = "INSERT INTO order_item (order_id, flower_id, quantity, price_at_order) VALUES (?, ?, ?, ?)"; // 最后清空购物车:DELETE FROM cart WHERE user_id = ?

血泪经验:事务必须包裹全部三步操作。曾有同学只对扣库存加事务,结果库存扣了但订单没生成,造成数据不一致。本项目OrderServlet中conn.setAutoCommit(false)+try-catch-finally手动 commit/rollback,是 JavaWeb 事务教学的经典写法。

4.3 后台管理功能:AdminServlet 如何实现“所见即所得”的 CRUD

管理员在/admin/flower_list.jsp页面看到的表格,由AdminServlet的doGet方法驱动:

// AdminServlet.java protected void doGet(HttpServletRequest request, HttpServletResponse response) { String action = request.getParameter("action"); // "list", "add", "delete", "update" switch (action) { case "list": List<Flower> flowers = flowerDAO.findAll(); // 查询全部鲜花 request.setAttribute("flowers", flowers); request.getRequestDispatcher("/admin/flower_list.jsp").forward(request, response); break; case "delete": int id = Integer.parseInt(request.getParameter("id")); flowerDAO.delete(id); // 物理删除(注意:生产环境应软删除) response.sendRedirect("AdminServlet?action=list"); break; // ... 其他 case } }

玄学提醒:JSP 页面中${flower.name}能正常显示,前提是Flower类的 getter 方法命名严格遵循 JavaBean 规范(如getName()对应name属性)。曾有同学把getFlowerName()写成getFlowername(),EL 表达式就失效——这是新手最常踩的“命名大小写”坑。


5. 避坑 / 常见问题 / 排查:95分项目里藏着的 4 个隐形雷区

5.1 现象:登录后跳转到空白页或 404,控制台无报错

原因:web.xml中<welcome-file-list>未正确指向login.jsp,或login.jsp文件实际路径不在WebContent/根目录下(比如误放进了WebContent/pages/子目录)
解决:检查web.xml内容,确认<welcome-file>login.jsp</welcome-file>;同时在 IDEA 的 Project 视图中展开WebContent,确认login.jsp文件图标是蓝色 JSP 图标(非普通文本),且路径为WebContent/login.jsp

5.2 现象:添加商品时提示“数据库连接失败”,但jdbc.properties明明配置正确

原因:MySQL 8.0+ 默认认证插件改为caching_sha2_password,而项目使用的mysql-connector-java-5.1.47.jar(常见于老项目)不支持该插件
解决:
① 方案一(推荐):降级 MySQL 用户认证方式

ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'your_password'; FLUSH PRIVILEGES;

② 方案二:升级 JDBC 驱动到mysql-connector-java-8.0.33.jar,并修改jdbc.properties中driver=com.mysql.cj.jdbc.Driver(已存在,无需改)

5.3 现象:购物车添加成功,但刷新页面后商品消失

原因:CartServlet中添加购物车逻辑未校验用户登录态,或cart表未关联user_id(部分简化版项目用 Cookie 存储购物车,但本项目是数据库存储)
解决:检查CartServlet的doPost方法,确认有HttpSession session = request.getSession(); User user = (User) session.getAttribute("user");,且插入cart表时user_id字段值来自user.getId(),而非null或0

5.4 现象:订单支付后,后台订单状态仍为“待支付”,未自动变更为“已支付”

原因:项目未集成真实支付网关,PayServlet仅模拟支付成功(如request.setAttribute("msg", "支付成功");),但未调用OrderDAO.updateStatus(orderId, "paid")
解决:找到PayServlet.java,在其doPost方法末尾添加:

int orderId = Integer.parseInt(request.getParameter("orderId")); orderDAO.updateStatus(orderId, "paid"); // 确保此方法存在且 SQL 正确 request.setAttribute("msg", "支付成功"); request.getRequestDispatcher("/pay_success.jsp").forward(request, response);

5.5 现象:中文乱码——URL 参数、表单提交、数据库存储全是问号

原因:Tomcat 默认 ISO-8859-1 解码 GET 请求,而项目未统一设置 UTF-8
解决:三处必须同步:
①web.xml中配置CharacterEncodingFilter(若无,手动添加):

<filter> <filter-name>CharacterEncodingFilter</filter-name> <filter-class>org.apache.catalina.filters.SetCharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> </filter> <filter-mapping> <filter-name>CharacterEncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>

②server.xml中 Connector 添加URIEncoding="UTF-8":

<Connector port="8080" protocol="HTTP/1.1" URIEncoding="UTF-8" ... />

③ MySQL 连接 URL 确保含characterEncoding=utf8(已存在,但需确认无拼写错误)


6. 进阶技巧:把“95分项目”变成“面试作品集”的 3 个改造方向

6.1 方向一:用 JUnit 4 补齐 DAO 层单元测试(让代码可信度翻倍)

光能跑不算强,能测才叫稳。给FlowerDAO写一个基础测试,验证库存扣减逻辑:

// test/dao/FlowerDAOTest.java public class FlowerDAOTest { private FlowerDAO dao; @Before public void setUp() { dao = new FlowerDAO(); // 使用真实数据库连接(非 mock) } @Test public void testUpdateStock_Success() throws SQLException { int flowerId = 1; // 假设 ID=1 的商品库存为 10 int quantity = 3; boolean result = dao.updateStock(flowerId, quantity); assertTrue(result); // 应返回 true // 再查一次库存,确认减少 Flower flower = dao.findById(flowerId); assertEquals(7, flower.getStock()); // 原10 - 3 = 7 } @Test public void testUpdateStock_InsufficientStock() throws SQLException { int flowerId = 1; int quantity = 100; // 超出当前库存 boolean result = dao.updateStock(flowerId, quantity); assertFalse(result); // 应返回 false } }

关键点:测试用例必须覆盖“成功”和“失败”两种分支,且失败场景(库存不足)的断言不能省略。面试时展示这个测试类,比口头说“我写了 DAO”有力十倍。

6.2 方向二:用 Bootstrap 4 重构前端界面(告别原始 HTML 表格)

原项目 JSP 页面多用<table>+<tr><td>布局,视觉陈旧。用 Bootstrap 4 替换只需三步:

  1. 在WebContent/WEB-INF/web.xml中添加静态资源映射(若无):
    <servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>/css/*</url-pattern> <url-pattern>/js/*</url-pattern> <url-pattern>/images/*</url-pattern> </servlet-mapping>
  2. 下载 Bootstrap 4 CSS/JS,放入WebContent/css/bootstrap.min.css、WebContent/js/bootstrap.min.js
  3. 修改login.jsp头部引入:
    <link rel="stylesheet" href="${pageContext.request.contextPath}/css/bootstrap.min.css"> <script src="${pageContext.request.contextPath}/js/bootstrap.min.js"></script>
    然后将表单包裹进 Bootstrap Card:
    <div class="container mt-5"> <div class="card mx-auto" style="max-width: 400px;"> <div class="card-header text-center">鲜花商城登录</div> <div class="card-body"> <form action="LoginServlet" method="post"> <div class="form-group"> <input type="text" name="username" class="form-control" placeholder="用户名" required> </div> <div class="form-group"> <input type="password" name="password" class="form-control" placeholder="密码" required> </div> <button type="submit" class="btn btn-primary btn-block">登录</button> </form> </div> </div> </div>

后悔药:别等面试前一周才改 UI。现在花 2 小时重构登录页和商品列表页,面试时打开浏览器演示,HR 看到“响应式布局”“现代配色”会立刻觉得你有工程意识。

6.3 方向三:增加日志输出与异常追踪(暴露你的 Debug 能力)

原项目异常处理多为e.printStackTrace(),不利于定位问题。用 Log4j 2 替换:

  1. 下载log4j-api-2.20.0.jar和log4j-core-2.20.0.jar,放入WebContent/WEB-INF/lib/
  2. 在src/下新建log4j2.xml:
    <?xml version="1.0" encoding="UTF-8"?> <Configuration status="WARN"> <Appenders> <Console name="Console" target="SYSTEM_OUT"> <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/> </Console> </Appenders> <Loggers> <Root level="info"> <AppenderRef ref="Console"/> </Root> </Loggers> </Configuration>
  3. 修改LoginServlet:
    import 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(HttpServletRequest request, HttpServletResponse response) { try { // 原有逻辑... } catch (Exception e) { logger.error("Login failed for user: {}", username, e); // 带参数和堆栈 request.setAttribute("msg", "系统繁忙,请稍后再试"); request.getRequestDispatcher("/login.jsp").forward(request, response); } } }

我的习惯:每次接手一个老项目,第一件事就是加日志。没有日志的系统就像没有仪表盘的汽车——你能开,但不知道油量、水温、转速。面试官问“你怎么排查线上问题?”,你掏出log4j2.xml和一段logger.error日志截图,比说一百句“我会看日志”都管用。

希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询