简介:JSP超市管理系统是一套B/S模式的Java Web完整项目,基于JSP、Servlet与MySQL实现,开发环境为MyEclipse8.5和Tomcat7.0,适合正在学习JSP编程、需要课程设计或毕业设计参考的读者。系统覆盖人事、销售、进货、库存、客户五大管理模块:人事方面可查询和更新职工、供货商信息;销售模块支持销售信息查询与商品盘点;进货与库存模块均提供信息查询及增删改查;客户管理可添加、删除用户并修改密码,功能划分清晰,贴近中小超市真实运营流程。压缩包约1.06MB,文件总数平台标注为0,文件类型明细暂未提供;包内配有完整Java源代码与MySQL数据库脚本,数据库配置信息已在代码中标注,部署后使用管理员账号admin/admin登录即可运行。目前已有1023人学习,读者通过阅读源码和库表脚本,可理解JSP页面到后台Servlet/DAO的调用链路、MySQL表结构设计以及基础CRUD写法,并能在现有模块上继续扩展,作为实训或二次开发起点。
1. 这篇文章写给谁:被“JSP超市管理系统”这个题目卡住的人
如果你手上正拿着“JSP超市管理系统”这类题目,多半是课程设计或者毕业设计的档期到了。这个题目的本质,是用 JSP 做页面、Servlet 写控制逻辑、MySQL 存数据、MyEclipse 当开发工具,最后凑出一个能在 Tomcat 里跑的 Web 项目。它不算新,但每年都有大量学生和转行新手栽在环境配置和数据库连接这些环节上。这篇文章就把从建表到部署的完整路径拆给你看,包括每一段代码为什么这样写,以及那些不跑一遍就根本发现不了的坑。
这套技术栈在今天依然值得做,原因很实际:学校验收看的是“能不能跑、代码是不是自己写的、答辩能不能说清楚”,而这套组合的每一层都简单到可以逐行解释。你不用去追 Spring Boot,先把 JSP + Servlet + MySQL 这条链路跑通,后面理解任何 Java Web 框架都会快很多。适合三类人:正在做课程设计的学生、想补 Java Web 基础课的新手、以及需要快速搭一个内部小系统的在职开发者。虽然是老技术,但逻辑一点没老。
2. MyEclipse 里的技术选型:为什么这套组合在课程设计里经久不衰
2.1 先搞清楚 JSP、Servlet、JavaBean 在项目里分别干什么
很多人拿到题目就四处找源码,结果下载一个压缩包,导入 MyEclipse 后一堆红叉,根本跑不起来。问题不在于项目本身,而在于没弄清楚这套 Web 结构里每个部件的位置。JSP 是展示层,它的本质是一个会被容器翻译成 Servlet 的页面文件,你在上面写 HTML,嵌入 Java 代码片段(Scriptlet)或者 JSTL 标签,浏览器最终拿到的是它生成的纯 HTML。Servlet 是控制层,负责接收请求、调用业务方法、决定跳转到哪个 JSP。JavaBean(或者现在更常用的普通 POJO)是数据模型,对应数据库里的表结构。
超市管理系统的“管理”二字,落在代码上就是增删改查。商品要增加、修改、下架,类别要维护,销售单要记录。这些操作全部可以归纳成“请求到达 Servlet → Servlet 调用 DAO → DAO 执行 SQL → 返回结果给 JSP 展示”。MyEclipse 在这个链条里的作用是帮你生成项目骨架:它建出来的 Web Project 自带src目录和WebRoot(也叫webapp)目录,前者放 Java 类,后者放 JSP 和静态资源。你不需要手动配置太多东西,但得知道WebRoot/WEB-INF/web.xml是整条链路的入口。
2.2 MyEclipse 自带 JDK 和 Tomcat 的匹配关系,容易埋雷
MyEclipse 版本不同,自带的 JDK 和 Tomcat 版本也不一样。比如 MyEclipse 2014 左右常配 JDK 1.7,而它在 Tomcat 8 下就需要对应版本的 Servlet 3.1 规范。很多新手拿到项目就点 Run on Server,发现 404 或者启动报错,原因往往是 MyEclipse 默认绑定的 Tomcat 目录和项目编译级别不一致。我一般会先看项目的 Java Compiler 级别,再确认 Tomcat 版本,两者至少保证一个大版本内的兼容,代码里用到javax.servlet的 API 才不会被 IDE 标红。
另外一个容易忽视的问题是 MySQL 驱动 jar 应该放在哪里。很多教程说要把mysql-connector-java-x.x.x-bin.jar放到WEB-INF/lib目录下,这是对的,因为 Tomcat 启动 Web 应用时会加载这个目录下的所有 jar。但不少人会顺手把它也放进 JDK 的jre/lib/ext或 MyEclipse 的common/lib,这会导致版本冲突——多个类加载器同时加载同名驱动类时,第 12 章 ClassNotFound 的报错就出来了。我的习惯是只在WEB-INF/lib放一份,并且在项目的 Java Build Path 里也加上这个 jar,让 IDE 编译时能识别com.mysql.jdbc.Driver这个类。
2.3 建项目的第一个动作:New Web Project 之后的目录调整
打开 MyEclipse,File → New → Web Project,输入项目名SupermarketMS。注意 MyEclipse 会问你要不要自动生成web.xml,一定要选生成,因为后面配置 Servlet 映射全靠它。建完以后立刻做三件事:一是检查src目录是否被标记为源代码根目录,否则新建的包会跑到别的地方去;二是把默认的index.jsp删掉或者改写成登录页;三是在WEB-INF/lib下放好 MySQL 驱动。这些动作做完,项目骨架才算立住了,不要急着写代码。
项目结构最好按包名分层建:com.supermarket.servlet放控制器,com.supermarket.dao放数据库访问对象,com.supermarket.model放实体类,com.supermarket.util放 DBUtil 工具类。这样分层的好处是答辩时老师问“你这个项目架构是什么样的”,你可以直接指着包结构讲清楚,也方便后续加功能。很多下载来的项目之所以乱,就是把所有类都堆在src根包下面,一搜几十个 java 文件全在默认包里,连包名都没有。
提示:如果你不确定项目是用 JDK 1.7 还是 1.8 编译的,检查
.classpath文件里的container配置,或者直接看项目属性里的 Java Build Path。这决定了你的代码能不能用 lambda 表达式和 try-with-resources。
3. 超市管理系统的数据库设计:5 张表和不能省的外键约束
3.1 先画清楚业务再建表:商品、类别、销售、供应商、用户
数据库设计是这类题目的分水岭。优秀的答辩现场,老师一定会问“表和表之间的关系是什么、为什么这样设计”。超市管理系统的核心表,我会拆成 5 张:用户表负责登录,类别表负责商品分类,商品表是主资产,销售记录表是流水账,供应商表补充商品来源。前 4 张必须建,供应商表看题目要求,但建上会显得业务模型更完整。每张表的主键统一用自增id,商品表的数量字段要极其谨慎,后面会细说。
先写出建表 SQL。注意 MySQL 的存储引擎要选 InnoDB,因为事务和外键是这套系统后面保证数据一致性的基础。MyISAM 在旧教程里出现频率很高,但它不支持事务,销售记录一旦写入失败,库存和流水就对不上了。字符集统一utf8,排序规则用utf8_general_ci就够,不追 utf8mb4(除非商品名要存 emoji,超市项目里几乎没有这个需求)。
-- 用户表(管理员/收银员共用) CREATE TABLE t_user ( id INT AUTO_INCREMENT PRIMARY KEY, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(64) NOT NULL, real_name VARCHAR(50) COMMENT '真实姓名,用于显示收银员', role TINYINT DEFAULT 1 COMMENT '1=管理员, 2=收银员', create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- 商品类别表 CREATE TABLE t_category ( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(50) NOT NULL, sort INT DEFAULT 0 COMMENT '排序权重,小的在前' ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- 供应商表 CREATE TABLE t_supplier ( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(100) NOT NULL, contact VARCHAR(50) COMMENT '联系人', phone VARCHAR(20) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- 商品表 CREATE TABLE t_product ( id INT AUTO_INCREMENT PRIMARY KEY, category_id INT NOT NULL, supplier_id INT DEFAULT NULL, name VARCHAR(100) NOT NULL, price DECIMAL(10,2) NOT NULL COMMENT '售价', cost DECIMAL(10,2) DEFAULT NULL COMMENT '进价,利润统计用', stock INT NOT NULL DEFAULT 0, status TINYINT DEFAULT 1 COMMENT '1上架, 0下架', CONSTRAINT fk_product_category FOREIGN KEY (category_id) REFERENCES t_category(id), CONSTRAINT fk_product_supplier FOREIGN KEY (supplier_id) REFERENCES t_supplier(id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- 销售记录表 CREATE TABLE t_sale ( id INT AUTO_INCREMENT PRIMARY KEY, product_id INT NOT NULL, user_id INT NOT NULL COMMENT '操作人', sale_price DECIMAL(10,2) NOT NULL COMMENT '成交单价,防止改价后对不上', quantity INT NOT NULL COMMENT '数量', total_amount DECIMAL(10,2) NOT NULL, sale_time DATETIME DEFAULT CURRENT_TIMESTAMP, CONSTRAINT fk_sale_product FOREIGN KEY (product_id) REFERENCES t_product(id), CONSTRAINT fk_sale_user FOREIGN KEY (user_id) REFERENCES t_user(id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;这段 SQL 里有几个细节不是随便写的。t_sale表里的sale_price是冗余字段——它把成交时的单价单独存了一份,而不是查询时关联商品表拿当前价,因为商品改价之后,历史销售记录不能被影响。这是系统设计里的一个经典陷阱,列出来是想告诉你数据表设计不能只满足“能查出来”。外键约束有人觉得麻烦,但在这个项目里必须留着:商品删不掉的时候实际上是外键在保护历史销售记录,比你在业务代码里一层层判断要可靠得多。
3.2 初始化数据:管理员账号和测试商品怎么给
建完表之后要写data.sql,否则项目一跑登录页面就报“用户不存在”,查了半天发现忘了做这一步。初始数据至少需要一条管理员账号,密码在真实项目中存 MD5 或加盐哈希,但课程设计阶段很多人直接明文。为了答辩时少被挑刺,我建议你至少用 MD5 存密码,Java 标准库MessageDigest就能算,不需要引第三方依赖。为什么不用更高级的 BCrypt?因为这个项目里你没有注册功能,密码只在初始化 SQL 里写一次,MD5 足够说明你意识到明文不安全这件事。
商品数据要提前造 10 条左右,类别 3 个,供应商 2 个。不然登录进去商品列表空空如也,JSP 页面上那个<c:forEach>循环一行都渲染不出来,你都不知道是页面写错了还是数据没插进去。造数据的 SQL 不要用随机数,固定写死几样东西:饮料、零食、日用的类别,具体的比如可乐、薯片、纸巾。这类数据贴近超市场景,答辩时好讲。
INSERT INTO t_category (name, sort) VALUES ('饮料', 1), ('零食', 2), ('日用品', 3); INSERT INTO t_supplier (name, contact, phone) VALUES ('本地食品批发', '王经理', '13800001111'), ('日用百货商行', '李经理', '13800002222'); INSERT INTO t_user (username, password, real_name, role) VALUES ('admin', 'e10adc3949ba59abbe56e057f20f883e', '系统管理员', 1); INSERT INTO t_product (category_id, supplier_id, name, price, cost, stock) VALUES (1, 1, '可口可乐 500ml', 3.50, 2.60, 120), (1, 1, '农夫山泉 550ml', 2.00, 1.20, 200), (2, 1, '乐事薯片 原味', 8.90, 6.50, 80);e10adc...这串就是123456的 MD5 值,你不需要找一个在线工具去现算,直接拿来初始化就行。如果你想让答辩更稳,可以在工具类里写一个MD5Util,登录时对用户输入的密码先加密再比对数据库,代码量不大但可以看出你考虑了安全。数据库这层设计到这里其实已经可以了,但前提是你建表时想清楚一个问题:库存扣减到底在哪一层完成?我放在下一章讲,因为这个问题决定了你看待整个代码结构的方式。
3.3 为什么说销售表和商品表不能合并成一张表
一个很多人会踩的设计坑,是把销售记录写成商品表里的“销量”字段:每卖一次就UPDATE t_product SET sold = sold + 1。这样做的问题很严重——你失去了所有历史细节:谁买的、谁操作的、什么时候买的、当时多少钱。超市管理系统的价值恰恰在这些流水数据上,将来做日报表、按月统计、商品排行,全依赖记录表。所以t_sale必须单独存在,t_product只保留当前库存,永远不要试图用一条 update 去记录历史。
聚合查询要用到 SUM 和 GROUP BY,这也是答辩必问题。拿“每天的销售额是多少”来说,SQL 写法是SELECT DATE(sale_time), SUM(total_amount) FROM t_sale GROUP BY DATE(sale_time)。这里用DATE()函数把 DATETIME 截断成日期,是 MySQL 里很常见的做法。排行类的统计也依赖这张表,比如“销量前十的商品”用ORDER BY SUM(quantity) DESC LIMIT 10就能取到。可以说,把t_sale剥离出来,整个系统的统计功能都有了下脚的地方。
4. 用 JSP + Servlet + DAO 把登录和商品管理跑通:三段核心代码
4.1 DBUtil:连接 MySQL 的 JDBC 代码是每个页面的前置条件
代码要从工具类写起,这个说法可能你已经听腻了,但它真的决定了后面所有功能能不能跑。DBUtil 类只需要做两件事:从配置文件读连接参数、返回一个 Connection。Java 世界里的老牌写法是DriverManager.getConnection(url, user, password),课程设计阶段够用。不需要上连接池,因为验收项目没有高并发压力,连接池反而会成为引入新问题的来源——比如 C3P0 的配置文件写错,账没还上等问题会莫名其妙地冒出来。
package com.supermarket.util; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; /** * JDBC 工具类:加载驱动、获取连接 */ public class DBUtil { private static final String URL = "jdbc:mysql://localhost:3306/supermarket_db?useUnicode=true&characterEncoding=utf8"; private static final String USER = "root"; private static final String PASSWORD = "123456"; static { try { Class.forName("com.mysql.jdbc.Driver"); } catch (ClassNotFoundException e) { throw new ExceptionInInitializerError("MySQL 驱动加载失败,检查 WEB-INF/lib 下是否有 mysql-connector-java.jar"); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(URL, USER, PASSWORD); } }这段代码有两个参数值得你记住。一是useUnicode=true&characterEncoding=utf8,它保证中文字段从数据库到页面的编码链路是通的,不加它会出现“商品名变成问号”这种让人抓狂的问题。二是Class.forName("com.mysql.jdbc.Driver"),虽然新版 JDBC 驱动支持 SPI 自动注册,但写静态初始化块是最兼容的写法——它能覆盖 MyEclipse 内置 Tomcat 和外部 Tomcat 的差异。static代码块保证了驱动只加载一次,不会因为多次加载而报警告。
4.2 登录过滤:Servlet 里写判断,不如在 Filter 里一劳永逸
登录功能每个人都能写出来:登录页提交表单到LoginServlet,查数据库,用户名密码对上了就把用户信息放 session。但“未登录时能不能直接访问商品管理页”这个问题,决定你被答辩老师追问的深度。最省事的做法是在每个 JSP 页面顶部写if (session.getAttribute("user") == null) response.sendRedirect("login.jsp")。这确实防住了,但是代码重复严重,而且一旦漏掉一个 JSP,这条防线就破了。更稳的做法是写一个LoginFilter,注册在web.xml里,所有/admin/*的请求过一层检查。
package com.supermarket.filter; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.io.IOException; public class LoginFilter implements Filter { public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) res; HttpSession session = request.getSession(false); // 不创建新 session boolean loggedIn = (session != null && session.getAttribute("user") != null); String requestPath = request.getRequestURI(); if (loggedIn || requestPath.endsWith("login.jsp") || requestPath.endsWith("LoginServlet")) { chain.doFilter(req, res); // 放行 } else { response.sendRedirect(request.getContextPath() + "/login.jsp"); } } }这段代码里注意request.getSession(false)这个细节——传false表示如果当前没有 session 就返回 null,而不是新建一个空的。很多教程写成getSession()不加参数,问题在于它会为每一个未登录的请求创建一个 session 对象,白白占内存。过滤器的落地方式是最常规的写法:通过web.xml配置<filter-mapping>,把/admin/*路径交给它管理。配完之后,你的商品管理页面全部放在/admin目录下面,拦截逻辑就生效了。
4.3 商品列表和新增商品:一个完整的 DAO + JSP 循环
商品管理是这套系统的核心功能,先看列表怎么呈现。做法是ProductServlet收到请求后,调用ProductDao.findAll(),把结果 List 放进 request 域,再forward到product_list.jsp。JSP 里用 JSTL 的<c:forEach>遍历,这个套路基本是固定搭配。值得写给你的是 DAO 里的查询方法,它决定了你的数据库操作是规规范范每行都关闭了的,还是留了一堆连接泄漏的。
package com.supermarket.dao; import com.supermarket.model.Product; import com.supermarket.util.DBUtil; import java.sql.*; import java.util.ArrayList; import java.util.List; public class ProductDao { public List<Product> findAll() throws SQLException { String sql = "SELECT * FROM t_product ORDER BY id DESC"; List<Product> list = new ArrayList<>(); // try-with-resources 保证 Connection/PreparedStatement/ResultSet 全部关闭 try (Connection conn = DBUtil.getConnection(); PreparedStatement ps = conn.prepareStatement(sql); ResultSet rs = ps.executeQuery()) { while (rs.next()) { Product p = new Product(); p.setId(rs.getInt("id")); p.setName(rs.getString("name")); p.setPrice(rs.getBigDecimal("price")); p.setStock(rs.getInt("stock")); list.add(p); } } return list; } public int insert(Product p) throws SQLException { String sql = "INSERT INTO t_product (category_id, supplier_id, name, price, cost, stock) VALUES (?,?,?,?,?,?)"; try (Connection conn = DBUtil.getConnection(); PreparedStatement ps = conn.prepareStatement(sql)) { ps.setInt(1, p.getCategoryId()); ps.setInt(2, p.getSupplierId()); ps.setString(3, p.getName()); ps.setBigDecimal(4, p.getPrice()); ps.setBigDecimal(5, p.getCost()); ps.setInt(6, p.getStock()); return ps.executeUpdate(); // 返回受影响行数 } } }这段代码里请务必用PreparedStatement而不是Statement。原因有两个:一是拼字符串 SQL 容易出错,商品名里有单引号直接就把语句折断了,轻则报语法错误,重则被 SQL 注入把表删了;二是?占位符让代码读起来清晰,别人接手也容易看懂。try-with-resources是 JDK 7 引入的语法,MyEclipse 里创建项目时如果把编译级别设成 1.6 会直接报语法错误,所以你得在项目属性里确认 Java Compiler 至少是 1.7。放在 WEB-INF/lib 下的那些旧 jar 不用管,编译级别才是关键。
5. JSP 超市管理系统开发中绕不开的高频 Bug:现象、原因、解法
5.1 MyEclipse 内置 Tomcat 启动慢或直接启动失败
现象:在 MyEclipse 里点 Run on Server,Console 卡在“Deployment”或者报出java.lang.OutOfMemoryError: PermGen space。原因:MyEclipse 自带 Tomcat 的 JVM 参数默认较小,而你的项目部署后需要加载多个 jar 和大量 JSP 编译产物,内存不够用。还有一个常见原因是web.xml里的<display-name>或<servlet-mapping>写错,导致部署文件释放时卡住。解决:打开 MyEclipse 的 Servers 视图,双击已有的 Tomcat 实例,在 Args 里加一行-XX:MaxPermSize=256m(JDK 8 不需要,但还是建议把初始堆调大点)。另外,把你自己的项目从部署列表里移除,重新 Add Deployment,很多时候是上次残留的半部署状态卡住了服务器。如果还不行,删掉 Tomcat 实例重新建一个,5 分钟解决,不用花时间研究底层。
5.2 登录页提交后跳转不过去,URL 路径 404
现象:login.jsp 输入账号密码点登录,浏览器地址栏变成http://localhost:8080/SupermarketMS/LoginServlet,然后显示 404 或者直接下载了一个文件。原因:表单的 action 写的是相对路径LoginServlet,缺少/开头的项目上下文路径(context path)。解决:JSP 顶部引入 JSTL 的<c:url>处理,或者直接写${pageContext.request.contextPath}/LoginServlet。这个表达式会自动把/SupermarketMS补上,是 JSP 页面写链接最不容易出错的写法。别用相对路径“../”,只要目录结构一调整,全盘就乱套了。
5.3 数据库插入中文变成问号(???)或被截断
现象:商品名称“可口可乐”插入后变成“???”,或者报Incorrect string value异常。原因:编码链路断裂。这条链有四环:MySQL 表字符集、JDBC 连接 URL 的characterEncoding、JSP 页面本身的pageEncoding、以及 Servlet 里对 request 设置的编码。任何一环不是 UTF-8,中文就妥妥出问题。解决:建表时用CHARSET=utf8(见上文建表语句);JDBC 的 URL 上带useUnicode=true&characterEncoding=utf8;在所有 Servlet 处理 POST 请求时调用request.setCharacterEncoding("UTF-8");JSP 文件头写上<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>。四样全配齐,才算是把这条链路锁死。注意 MySQL 版本如果装了 8.x,表默认字符集是 utf8mb4,JDBC 连接参数里的characterEncoding=utf8依然有效,但如果之前建库时用了latin1,光改连接参数救不回来,必须ALTER TABLE t_product CONVERT TO CHARACTER SET utf8mb4。
5.4 MySQL 驱动版本不匹配:JDBC 连接时报 No suitable driver
现象:点登录按钮后报java.sql.SQLException: No suitable driver found for jdbc:mysql://localhost:3306/...。原因:两个最常见:驱动 jar 不在WEB-INF/lib下,或者 jar 是 MySQL 8.x 的com.mysql.cj.jdbc.Driver,而你的代码里写的还是老式驱动类。MyEclipse 2014 时代对应的驱动 jar 通常是 5.1.x 系列,加载类名是com.mysql.jdbc.Driver。如果你下载的是新版mysql-connector-java-8.0.x.jar,这个类名已经改了,需要同时修改 DBUtil 里的Class.forName("com.mysql.cj.jdbc.Driver"),并且 URL 上追加serverTimezone=Asia/Shanghai。解决:优先检查 jar 位置,其次核对驱动类名,最后确认 MySQL 服务端口确实是 3306 而没有被本机占用。命令行里netstat -ano | findstr 3306(Linux 用ss -lntp | grep 3306)一看便知。
5.5 JSP 页面报错 The method getParameter() is undefined for the type HttpServletRequest
现象:代码明明没错,但 MyEclipse 里一片红。原因:这个最诡异——项目没有把 Tomcat 的 runtime 库关联上,导致 IDE 找不到javax.servlet接口。解决:右键项目 → Properties → Targeted Runtimes,勾上你的 Tomcat 版本,点 Apply。如果 Runtimes 列表是空的,先去 Window → Preferences → Server → Runtime Environments 把 Tomcat 路径配好。这类环境问题占了这类项目报错的一大半,代码本身反而是最不需要担心的部分。
6. 从能跑到像样的项目:三个细节让答辩局面完全不一样
把商品 CRUD 跑通、登录拦截做好,这已经达到“及格线”。但如果想从“能跑”进阶到“能讲、经得起追问”,我建议你再补三个能力点,全部落在现有代码结构上,不需要大改。第一个是把销售额统计做成一个简单的报表页;第二个是把密码从明文改为 MD5 加盐校验;第三个是商品删除改成逻辑删除而不物理删行。三件事的代码量都不大,但每件都能在答辩时成为展示系统设计意识的证据。
报表页的工程量其实很小:新增一个SaleServlet,查询SELECT DATE(sale_time) AS d, SUM(total_amount) AS total FROM t_sale GROUP BY d ORDER BY d DESC,把结果放在 request 域里转给report.jsp。这个功能可以证明你“会用 GROUP BY 聚合”,比在 SQL 语法层面被老师提问要主动得多。逻辑删除的做法是给t_product加一个status字段,删除时更新状态。查询商品列表时 SQL 条件带上WHERE status = 1,这样历史销售记录里的外键关联不会断裂,你也顺便解释了为什么建表时外键是必要的。
最后说一个很多人忽略的习惯:项目里留一份README.md或部署说明.txt,把 JDK 版本、Tomcat 版本、MySQL 初始化脚本路径、默认管理员账号写清楚。这个文件在提交作业、换电脑继续开发、发给同学帮忙看 bug 这三个场景里都是救命稻草。我自己在带学生的过程中注意到,能写清楚部署文档的人,对项目的理解至少扎实一层——毕竟当你用文字把“怎么跑起来”写给别人时,你自己对每个环节都会更敏感。希望这篇拆解能帮你少走几段弯路,顺利把项目交掉。
作为一个带过不少课程设计的人,我最后想提醒你一句:不要拿到题目就急着找现成源码,这年头下载来的“源码”不是缺数据库脚本,就是有路径写死的绝对地址,改起来比从零写还痛苦,这种亏我是吃过的。希望这篇文章里给到你的方法和坑位,能让你避免再踩类似的坑,一次把项目真正做出来。
本文还有配套的精品资源,点击获取