简介:这是一份基于JSP+Servlet+MySQL的网上在线购物系统期末课程设计项目,面向JavaWeb初学者和需要提交期末作业的学生。项目已在MyEclipse中整理完毕,内置前端页面、后端业务逻辑与数据库脚本,拿来即可运行;使用前只需修改课程报告中的学号姓名等个人信息,代码无需调整。压缩包共2000个文件,大小33.8MB,包括484个html、436个css、342个js等前端静态资源,77个java与77个class构成的Servlet控制层,43个jsp动态页面,以及jar依赖、sql脚本、xml配置等,结构清晰,便于对照学习。项目覆盖商品展示、购物车、订单、后台管理等典型电商模块,控制器、业务层和工具类分模块组织,适合快速理解JSP+Servlet+MVC开发流程,也可作为课程设计报告和答辩的实操基础。已有99人学习下载,适合拿来即用的期末课程设计参考。
1. 期末周拿到 JavaWeb 课程设计压缩包:网上在线购物系统该怎么下手
期末周还剩两天,你刚解压完一份「JavaWeb期末课程设计(JSP+Servlet)(mysql)-网上在线购物系统SSM+MyEclipse.rar」,里面一堆文件夹、若干个.java、一坨.jsp和一份从没打开过的.sql。这份网上在线购物系统课设包,本质上是一套包含手写 JSP+Servlet 版和 SSM 框架版两套实现的电商项目,数据库用 MySQL,工程文件由 MyEclipse 生成,只要环境对了就能直接部署到 Tomcat 跑起来。它最有价值的用法不是解压后原封不动交上去,而是花一个晚上把它跑通、读懂、再改掉其中一个模块——能讲清楚每个 Servlet 为什么存在,才是答辩拿高分的分水岭。这篇笔记就是按这个路径来的。
2. 读懂课设包的技术底牌:JSP+Servlet 与 SSM 的取舍
2.1 一套购物系统为什么会有 JSP+Servlet 和 SSM 两套写法
网上流传的 JavaWeb 期末课设包,解压后常见两种情况:要么是两个独立工程目录并列,一个叫shop_jsp、一个叫shop_ssm;要么是同一个工程里主代码用 JSP+Servlet 手写,局部模块引入了 Spring 系写法。标题里四个关键词并排出现,本质上是在告诉你:这份代码是「手写 MVC」和「框架版 MVC」的对照样本。
JSP+Servlet 版是 JavaWeb 课设的底线要求,也是答辩时最好讲的方案。请求先进 Servlet 的doGet/doPost,Servlet 调 DAO 层,DAO 用 JDBC 拼 SQL 访问 MySQL,最后forward到 JSP 渲染页面。整套流程没有黑匣子,断点打在哪、数据走到哪一步,全都能肉眼看见。缺点是代码量大,一个商品列表功能从 Servlet 到 DAO 要写四五个类,重复的 JDBC 样板代码到处都是。
SSM 版(Spring + SpringMVC + MyBatis)则把这些重复劳动压缩掉了。Controller 里一个方法就是一个接口,MyBatis 的 Mapper 接口加一条 XML 就能完成查询,事务由 Spring 声明式接管。代码少了,但要求你理解 IoC 容器、Mapper 代理、依赖注入这些概念——如果只是为了交课设,讲不清这些概念反而会被老师追问到卡壳。
我的建议很明确:时间不够就把主精力放在 JSP+Servlet 那套实现上,SSM 版当作一句加分话术来准备,例如「我还对比了 MyBatis 重构 DAO 层的方案,能减少大约 60% 的样板代码」。下表是两套方案的真实取舍。
| 对比项 | JSP+Servlet 版 | SSM 版 |
|---|---|---|
| 架构分层 | JSP + Servlet + DAO + JDBC | JSP + Controller + Service + Mapper |
| 核心依赖 | servlet-api、jstl、mysql-connector | spring-webmvc、mybatis、连接池 |
| 学习门槛 | 低,每个请求路径都看得见 | 中高,要先理解 IoC 与映射 |
| 答辩友好度 | 高,能讲清每一步在干什么 | 更高,但讲不清反而暴露短板 |
| 期末投入产出比 | 优先跑通 | 作为加分项展示 |
2.2 启动前先核验环境:JDK、Tomcat、字符集一个都不能少
说句血泪经验:课设包打开后满屏红叉,九成不是代码问题,是环境不匹配。MyEclipse 导出的工程对 JDK 版本、Target Runtime、字符集非常敏感,任何一项对不上,编译器都会先给你制造几十个假错误。
核验顺序按下面这张表来,从上到下一次过完,能省掉后面大半的排错时间。
| 检查项 | 建议配置 | 不匹配时的典型症状 |
|---|---|---|
| JDK | 1.8(老工程也可用 1.7) | 源码编译报错、UnsupportedClassVersionError |
| Tomcat | 与 JDK 对应的 8.x / 9.x | 启动闪退、Context 加载失败 |
| 工程字符集 | UTF-8 | 页面中文乱码、注释乱码 |
| MySQL | 5.7 或 8.x | 驱动类找不到、时区报错 |
| MyEclipse | 与导出版本相近即可 | 工程无法识别、缺失运行时 |
具体操作不复杂。启动 MyEclipse 后,先打开Window -> Preferences -> Java -> Compiler,确认编译级别是 1.8;再到Window -> Preferences -> Servers -> Runtime Environments里添加本机的 Tomcat,路径别选到 Tomcat 的二级目录;最后选中项目按Alt+Enter,在Resource面板看Text file encoding是否为 UTF-8。这三步做完,再谈导入工程。
2.3 MySQL 侧的表结构:用户、分类、商品、订单、订单明细五张核心表
网上购物系统的答辩环节,老师最爱问的不是「页面怎么跳」,而是「表怎么设计的」。哪怕代码是下载的,表结构你得能画出来、讲明白。一个满足课设要求又不至于过度设计的库,通常只有五张核心表,再按需加一张管理员表就够了。
| 表名 | 作用 | 关键字段 |
|---|---|---|
| user | 前台用户 | uid、username、password、nickname、create_time |
| category | 商品分类 | cid、cname |
| goods | 商品表 | gid、cid、gname、price、stock、pic、description |
| orders | 订单主表 | oid、uid、order_no、total、status、create_time |
| order_item | 订单明细 | item_id、oid、gid、quantity、price |
这里有一个容易踩的命名问题:订单表不要叫order,ORDER是 SQL 保留字,你写SELECT * FROM order会直接语法报错。常见的处理是表名用orders,字段名order_no又加唯一索引,因为一个用户的一次下单要对应订单主表一条记录、明细表多条记录,order_no是人工可读的订单号,而oid是自增主键。
还有一个容易被问懵的问题:「购物车怎么不建表?」。课设级别的购物车通常不落库,而是存在 Session 里,理由在第 4 章会展开——用户没点结算之前,购物车只是临时状态,没有必要写数据库。你只要在答辩时把「临时状态用 Session、持久状态用表」这句话说出来,老师基本不会再追问。
3. 把 rar 变成本地可运行:数据库初始化、JDBC 配置与 Tomcat 部署
3.1 先让 MySQL 有数据:初始化库表的 SQL 脚本与字符集参数
绝大多数课设包都会附带一份.sql,但直接导入经常会因为编码不对出现中文乱码。正确做法是先建库,指定字符集为utf8mb4,再切库执行建表语句。utf8mb4是utf8的超集,商品名称里万一有 Emoji 或者生僻字也不会存坏。
下面是这套网上在线购物系统最基本的初始化脚本,按顺序执行即可:
-- 建库:指定 utf8mb4,避免中文乱码,排序规则选 utf8mb4_general_ci CREATE DATABASE IF NOT EXISTS shop_db DEFAULT CHARACTER SET utf8mb4 DEFAULT COLLATE utf8mb4_general_ci; USE shop_db; -- 用户表:username 唯一,password 存处理后密文,不存明文 CREATE TABLE user ( uid INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(64) NOT NULL, nickname VARCHAR(50), create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB; -- 商品分类表 CREATE TABLE category ( cid INT PRIMARY KEY AUTO_INCREMENT, cname VARCHAR(50) NOT NULL ) ENGINE=InnoDB; -- 商品表:price 用 DECIMAL 存精确小数,不用 FLOAT CREATE TABLE goods ( gid INT PRIMARY KEY AUTO_INCREMENT, cid INT NOT NULL, gname VARCHAR(100) NOT NULL, price DECIMAL(10,2) NOT NULL, stock INT DEFAULT 0, pic VARCHAR(200), description TEXT, KEY idx_cid (cid) ) ENGINE=InnoDB; -- 订单主表:order_no 唯一,status 用 TINYINT 表示状态 CREATE TABLE orders ( oid INT PRIMARY KEY AUTO_INCREMENT, uid INT NOT NULL, order_no VARCHAR(32) NOT NULL UNIQUE, total DECIMAL(10,2) DEFAULT 0, status TINYINT DEFAULT 0, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, KEY idx_uid (uid) ) ENGINE=InnoDB; -- 订单明细表:一条订单对应多条商品记录 CREATE TABLE order_item ( item_id INT PRIMARY KEY AUTO_INCREMENT, oid INT NOT NULL, gid INT NOT NULL, quantity INT NOT NULL, price DECIMAL(10,2) NOT NULL, KEY idx_oid (oid) ) ENGINE=InnoDB;这段脚本的逻辑核心是两点:一是所有表都用 InnoDB,因为下单时要靠事务保证扣库存和写订单的一致性,MyISAM 不支持事务,用了会埋雷;二是price全部用DECIMAL(10,2),商品金额精确到分,用FLOAT/DOUBLE会出现 0.1+0.2 不等于 0.3 的浮点误差,这在答辩现场演示时非常尴尬。执行方式可以直接在命令行mysql -u root -p < init.sql,也可以在 Navicat 之类的图形工具里打开文件后整段运行。
提示:如果你拿到的
.sql文件是 Windows 记事本保存的,双击运行前先确认文件编码是 UTF-8 或 GBK 之一,否则中文注释和中文数据会以乱码形式写进表里。
3.2 JDBC 连接参数:驱动类、时区与 SSL 的三种报错根源
建好库表之后,代码能不能连上 MySQL,瓶颈几乎全在那串 JDBC URL 和驱动类名上。课设包里的JdbcUtils.java是连接入口,不同 MySQL 版本要用的驱动类不一样,这是新手最容易翻车的地方。
下面这个工具类是最常见、最稳的写法:
import java.sql.*; public class JdbcUtils { // useSSL=false 关闭 SSL 告警,serverTimezone 指定时区,编码指 UTF-8 private static final String URL = "jdbc:mysql://localhost:3306/shop_db?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai"; private static final String USER = "root"; private static final String PASSWORD = "改成你自己的数据库密码"; static { try { // MySQL 8.x 驱动类;5.x 用的是 com.mysql.jdbc.Driver Class.forName("com.mysql.cj.jdbc.Driver"); } catch (ClassNotFoundException e) { e.printStackTrace(); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(URL, USER, PASSWORD); } public static void close(Connection conn, Statement stmt, ResultSet rs) { // 关闭顺序:rs -> stmt -> conn,防止连接泄漏 if (rs != null) { try { rs.close(); } catch (SQLException e) { } } if (stmt != null) { try { stmt.close(); } catch (SQLException e) { } } if (conn != null) { try { conn.close(); } catch (SQLException e) { } } } }这段代码里最关键的三个参数分别是:characterEncoding=UTF-8保证中文正常存取;serverTimezone=Asia/Shanghai解决 MySQL 8.x 默认时区跟本地时区不一致导致的报错;useSSL=false关闭本地开发环境用不到的 SSL 握手。三个参数少任何一个,启动时都有可能抛出异常或者插入中文变问号。
注意:如果你的 MySQL 是 5.7,驱动类仍然写
com.mysql.jdbc.Driver没问题;如果是 8.x,建议把代码改成com.mysql.cj.jdbc.Driver,同时把mysql-connector-java的 jar 换成 8.x 对应版本。驱动 jar 必须放在WEB-INF/lib下,而不是只放在项目的 Build Path 里,否则部署到 Tomcat 后运行时依然找不到类。
3.3 Tomcat 部署三步:导入项目、绑定运行时、确认 context-root
数据库就绪、连接类就绪,接下来是把工程跑在 Tomcat 上。MyEclipse 里导入课设工程和普通 Eclipse 稍有不同,步骤固定为三步,顺序错了会出现各种奇怪问题。
第一步,确认 MySQL 服务已经启动。Windows 下可以用命令行快速验证:
# 查看 MySQL 是否在运行 net start | findstr -i mysql # 如果没看到结果,按服务名启动,MySQL80 是 8.x 的默认服务名 net start MySQL80第二步,导入工程。在 MyEclipse 里执行File -> Import -> Existing Projects into Workspace,目录选择解压后包含.project文件的那一层。如果导入时提示No projects are found,说明你选错了层级,往上一级或下一级找。导入后如果项目名带红色感叹号,执行Project -> Clean...让它重新编译一轮,大部分假红叉会消失。
第三步,绑定 Tomcat 运行时并启动。选中项目按Alt+Enter,在Targeted Runtimes里勾选本机已配置的 Tomcat;然后在Servers视图右键 Tomcat 选择Add and Remove...,把项目添加进去,最后启动 Tomcat。浏览器访问地址通常是:
http://localhost:8080/工程名/这里最容易踩的是 context-root 和工程名不一致的问题。MyEclipse 默认 context-root 与工程名相同,但有的课设包在.settings里改过部署名。右键项目Properties -> Web Context Root能直接看到实际值,改完之后要重启 Tomcat 才会生效。如果你访问 404,先查这里,别急着怀疑代码。
4. 核心业务代码拆解:登录会话、购物车与下单事务的落地写法
4.1 用 Filter 拦截未登录用户:登录鉴权的两种边界情况
网上在线购物系统的受保护资源是购物车、订单确认、个人中心这类需要登录才能访问的页面。用 Servlet 一个个判断登录状态太蠢,最常见做法是写一个过滤器,把未登录请求统一挡回登录页。
public class LoginFilter implements Filter { @Override public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) res; // 注意 getSession(false) 不会主动创建 Session,避免给每个匿名用户都种一个 Session HttpSession session = request.getSession(false); if (session == null || session.getAttribute("loginUser") == null) { // 未登录:跳回登录页,并带上来源路径,登录后可以跳回来 response.sendRedirect(request.getContextPath() + "/login.jsp?redirect=" + request.getRequestURI()); return; } // 已登录:放行,继续走业务 Servlet chain.doFilter(request, response); } }这段过滤器的关键细节是request.getSession(false)里的false。网上很多旧代码写成getSession(),会把所有游客都强制创建 Session,购物车逻辑一旦依赖 Session 就会混入大量空数据。放行条件判断的是 Session 里的loginUser属性,这个属性在登录成功时由 LoginServlet 写入。
在web.xml里配置过滤范围时要注意两个边界情况。第一个是过滤路径别覆盖到登录页本身,否则会死循环跳转;第二个是login.jsp里的静态资源(CSS、JS)最好也放行。常见配置如下:
<filter> <filter-name>LoginFilter</filter-name> <filter-class>com.shop.filter.LoginFilter</filter-class> </filter> <filter-mapping> <filter-name>LoginFilter</filter-name> <url-pattern>/cart/*</url-pattern> <url-pattern>/order/*</url-pattern> </filter-mapping>4.2 购物车用 Session 存而不是建表:按商品 ID 合并数量的实现
第 2 章留下一个问题:购物车为什么不用表?原因很简单——购物车是用户浏览过程中的临时状态,用户可能加购后直接关浏览器,也可能逛了半天一件都没结算。为临时状态建表意味着每次加购都要写一次库,课设项目完全没必要。标准做法是把购物车对象放进 Session,用户登录时创建,会话过期自动销毁。
购物车结构上,key是商品 ID,value是购物车条目,这样同一个商品反复加购时会自动合并数量:
public class Cart { // 用商品ID作 key:加购同一商品时合并数量,而不是追加两条记录 private Map<Integer, CartItem> items = new HashMap<>(); public void add(Integer gid, CartItem newItem) { CartItem old = items.get(gid); if (old == null) { items.put(gid, newItem); } else { // 合并数量,保留原单价 old.setQuantity(old.getQuantity() + newItem.getQuantity()); } } public void remove(Integer gid) { items.remove(gid); } public void clear() { items.clear(); } // 遍历条目计算总价,结算时以这个为准 public double getTotal() { double total = 0; for (CartItem item : items.values()) { total += item.getPrice() * item.getQuantity(); } return total; } }这里有两个容易写歪的地方。第一个是合并逻辑:有的同学加购时直接items.put(gid, newItem),把数量覆盖了,用户加两次购物车数量还是 1,这是典型的逻辑 bug。第二个是把总价的浮点运算直接double到底,合计金额出现 0.999999 之类的尾巴。稳妥的做法是金额都用BigDecimal,或者在CartItem里把价格字段直接用BigDecimal,getTotal()里也全程用BigDecimal,避免答辩演示时被老师看出浮点问题。
购物车的 Servlet 调用也比较固定:doPost里取gid和quantity参数,构造CartItem塞进Cart,然后session.setAttribute("cart", cart)。注意每次加购都要重新setAttribute,只改Cart对象内部状态而不写回 Session,页面刷新时读到的还是旧数据。
4.3 下单事务:扣库存与写订单必须绑定在同一个事务边界里
购物车只是热身,整个课设里最值得讲、也最容易被老师追问的,是下单操作。一次下单要做三件事:插入订单主表记录、批量插入订单明细、扣减商品库存。这三件事要么全成功,要么全回滚,否则就会出现「订单建了库存没减」或者「库存扣了订单没建」的脏数据。
用 JDBC 手写事务的常见写法是这样的:
Connection conn = null; try { conn = JdbcUtils.getConnection(); // 关闭自动提交,事务边界从这里开始 conn.setAutoCommit(false); // 1. 插入订单主表,返回自增主键 oid String insertOrder = "INSERT INTO orders (uid, order_no, total, status) VALUES (?, ?, ?, 0)"; PreparedStatement psOrder = conn.prepareStatement(insertOrder, Statement.RETURN_GENERATED_KEYS); psOrder.setInt(1, uid); psOrder.setString(2, orderNo); psOrder.setBigDecimal(3, total); psOrder.executeUpdate(); ResultSet keys = psOrder.getGeneratedKeys(); keys.next(); int oid = keys.getInt(1); // 2. 逐条插入订单明细 String insertItem = "INSERT INTO order_item (oid, gid, quantity, price) VALUES (?, ?, ?, ?)"; for (CartItem item : cartItems) { PreparedStatement psItem = conn.prepareStatement(insertItem); psItem.setInt(1, oid); psItem.setInt(2, item.getGid()); psItem.setInt(3, item.getQuantity()); psItem.setBigDecimal(4, item.getPrice()); psItem.executeUpdate(); } // 3. 扣库存:stock >= ? 条件防止超卖,影响行数为0说明库存不足 String deductStock = "UPDATE goods SET stock = stock - ? WHERE gid = ? AND stock >= ?"; PreparedStatement psStock = conn.prepareStatement(deductStock); for (CartItem item : cartItems) { psStock.setInt(1, item.getQuantity()); psStock.setInt(2, item.getGid()); psStock.setInt(3, item.getQuantity()); int rows = psStock.executeUpdate(); if (rows == 0) { // 库存不足,直接抛异常触发回滚 throw new RuntimeException("商品库存不足: " + item.getGid()); } } conn.commit(); } catch (Exception e) { if (conn != null) { conn.rollback(); } throw e; } finally { if (conn != null) { conn.setAutoCommit(true); conn.close(); } }这段代码里最值得在答辩时强调的是UPDATE goods SET stock = stock - ? WHERE gid = ? AND stock >= ?这个写法。它把「库存判断」和「库存扣减」合并成一条原子 SQL,而不是先SELECT stock判断再UPDATE。后者在两个请求并发时会读到同一个库存值,导致超卖;前者依靠数据库行锁和条件更新,从根上避免这个问题。
提示:如果你手头课设包里的下单代码是先查库存再更新,建议花十分钟改成上面这条 SQL。这是整篇代码里性价比最高的一次改动,既能防止逻辑错误,又能在答辩时主动讲出「我考虑了并发超卖」,这一个点就能拉高评分。
5. 购物系统课设避坑指南:红叉、404、乱码等 5 个高频事故
5.1 项目导入后满屏红叉,启动时报 ClassNotFound
现象:工程导入 MyEclipse 后,项目名带红叉,展开全是红色波浪线;强行走Run on Server,Tomcat 控制台抛java.lang.ClassNotFoundException: com.mysql.cj.jdbc.Driver,或者报某个 Servlet 类找不到。
原因:绝大多数情况是 JDK 编译级别和 Tomcat 运行时版本与工程配置不一致,导致 MyEclipse 无法解析工程依赖;驱动类找不到则是mysql-connector-java.jar没被复制到WEB-INF/lib,只在 Build Path 里引用了源码路径,部署时没用上。
解决:先Project -> Clean清一次编译缓存;再检查Properties -> Java Compiler的编译级别和Properties -> Targeted Runtimes里的 Tomcat;最后打开WebContent/WEB-INF/lib目录,确认驱动 jar 真实存在,不存在就把驱动包直接拖进去,然后重新部署。
5.2 Tomcat 正常启动,访问项目却是 404
现象:Tomcat 启动日志里没有任何报错,访问http://localhost:8080/工程名/却显示 404,但访问 Tomcat 默认主页正常。
原因:工程没有被部署到 Tomcat,或者部署后的 context-root 和实际访问路径不一致。MyEclipse 里有多个 Tomcat 实例时,项目可能被部署到了另一个 Tomcat 上;context-root 被改过也是常见原因。
解决:在Servers视图双击当前 Tomcat,打开Modules页签看部署列表里是否有这个工程,Path 列就是 context-root。没有就点Add Web Module加上;路径不对就点Edit改成实际想要的路径。改完重启 Tomcat 再看一次。
5.3 数据库连不上:驱动类与时区是最常见的两类根源
现象:启动后访问商品列表,页面报 500,控制台堆栈里有Communications link failure或The server time zone value '�й���ʱ��' is unrecognized。
原因:前者通常是 MySQL 8.x 的驱动连接串没加serverTimezone参数,或者驱动 jar 版本和 MySQL 版本不匹配;后者是连接串里的时区参数值写错,用了系统不认的时区名。
解决:先把驱动 jar 升级到和 MySQL 大版本对应的版本,再确认连接串完整:serverTimezone=Asia/Shanghai是最通用的时区写法;如果 MySQL 服务器端口不是默认 3306,要在连接串里改成实际端口,localhost:3307这种端口错误同样会报连接失败。用户名和密码单独排查,root的密码如果含特殊字符,记得做 URL 转义。
5.4 中文乱码:页面、连接串、数据库三层编码不一致
现象:页面商品名称显示成???或者类似��的乱码;后台插入数据库的数据在 SQL 客户端里看也是乱码。
原因:三层编码不一致。JSP 页面没声明 UTF-8、JDBC 连接串缺characterEncoding=UTF-8、数据库表用的是latin1编码,这三处只要有一处对不上,中文就会在传输过程中变质。
解决:按顺序排查。第一步,打开所有 JSP,确认开头有pageEncoding="UTF-8";第二步,检查JdbcUtils里连接串有useUnicode=true&characterEncoding=UTF-8;第三步,用SHOW CREATE TABLE goods看表字符集,不是utf8mb4就执行ALTER TABLE goods CONVERT TO CHARACTER SET utf8mb4。另外web.xml里加一个字符集过滤器,统一把所有请求的编码设成 UTF-8,是最彻底的解决办法。
5.5 商品图片全部不显示:路径没有站在 Web 容器的视角写
现象:页面结构正常,文字都能显示,但所有商品图片都是裂图,查看浏览器控制台,图片资源报 404。
原因:课设代码里图片路径写了绝对磁盘路径,比如D:/upload/a.jpg,或者写在源码src目录下的图片发布后压根没被复制到部署目录。浏览器最终访问的路径是http://localhost:8080/工程名/xxx.jpg,这个路径只存在于 Web 容器的部署目录里,和源码目录不是一回事。
解决:把图片放到WebContent/upload/目录下,JSP 里用${pageContext.request.contextPath}/upload/xxx.jpg拼接,这样部署到哪个路径都能自适应。如果图片是运行时上传的,正确做法是在 Tomcat 里配置虚拟目录映射到磁盘路径,而不是直接在页面里写死D:/。这个坑改起来不难,但几乎是课设项目图片问题的通病。
6. 能跑只是及格,能讲才是加分:答辩验证清单与三个必做小改造
把系统跑通之后,别急着关机,先按下面的清单走一遍手工验证,每一行都代表答辩时老师可能现场点的操作。
| 验证场景 | 操作方式 | 预期结果 |
|---|---|---|
| 未登录访问购物车 | 直接输入/cart/listURL | 跳转登录页,不报 500 |
| 普通用户越权 | 修改订单 URL 中 oid | 无法查看他人订单 |
| 库存不足下单 | 把某商品库存改成 1,购物车加 2 件结算 | 提示库存不足,订单未生成 |
| SQL 注入尝试 | 搜索框输入' OR 1=1 -- | 返回空结果,不报错 |
| 重复提交订单 | 结算页快速点两次提交 | 只生成一笔订单 |
这三个必须做的小改造,按投入产出比排序:
第一个,把 JSP 里所有的 JDBC 代码挪到 DAO 层。很多下载的课设把Connection直接写在 JSP 的<% %>里,答辩老师翻开页面源码一眼就能看出来,这一下印象分就没了。只需要复制粘贴到单独的GoodsDao.java,JSP 里改成调用 DAO 方法,半小时搞定。
第二个,把登录密码从明文改成哈希存储。如果课设包里的user表密码是明文,至少把注册逻辑改成存储哈希值,登录时比对哈希结果。不用引入框架,JDK 自带的MessageDigest就能算 SHA-256,代码量不超过十行,但「密码不落明文」这个意识在答辩里是实打实的加分项。
第三个,给商品列表加分页。SELECT * FROM goods LIMIT offset, size加两个参数就能实现,前端页面加一个上一页/下一页的链接。分页是老师最爱问的功能点之一,做了就能聊,没做就只能答「没做」。
最后说一个我的教训。之前帮一位同学看课设,功能全跑通了,但搜索框输入一个单引号直接报 SQL 语法错误,原因是代码里用的是字符串拼接 SQL。从那之后我养成一个习惯:课设代码里只要出现 SQL 拼接,一律改成PreparedStatement占位符;页面里只要输出用户输入的内容,一律做转义。这两条不改,系统跑得再顺也可能在关键时刻翻车。希望这篇笔记能帮你在期末周少走几条弯路,顺利把这份网上在线购物系统变成自己真正讲得清的课设。
本文还有配套的精品资源,点击获取