JavaWeb蛋糕店系统:完整业务闭环的课程设计实战项目
简介这是一套完整的JavaWeb蛋糕店网站系统课程设计资源面向计算机相关专业在校学生、教师及初学者解决课程设计、毕业设计与Web开发入门实践需求。资源包含可直接运行的源码、界面演示视频、详细文档说明、MySQL数据库SQL脚本及全量中文注释覆盖前后台核心功能前台支持商品展示推荐/分类/搜索、购物车管理、用户注册登录与订单查询后台提供管理员商品与订单管理模块。压缩包共586个文件含78个JSP页面、55个Java业务类、110个编译后Class文件、48个PNG/JPG图片资源、28个JS交互脚本及2个SQL建库脚本整体17.4MB结构清晰、注释详尽便于理解MVC分层逻辑与ServletJDBC技术栈实现细节。已有694人下载学习代码经实际部署测试答辩平均分96分适合作为毕设参考、课设原型或二次开发基础框架。1. 这不是又一个“Hello World”JavaWeb项目它是一套能直接跑通、带完整业务闭环的蛋糕店网站系统专为课程设计和毕设答辩打磨过96分你是不是也经历过——在老师布置“JavaWeb课程设计”时翻遍CSDN、GitHub、某宝下载了十几个“蛋糕店”“水果店”“书店”源码解压后发现web.xml里 servlet 映射路径写错404 到怀疑人生MySQL 表字段名和 Java 实体类属性对不上NullPointerException报在setPrice()那一行前台购物车加不进去后台登录跳转死循环连首页 banner 都加载不出来README 只有一行“导入数据库即可运行”但 SQL 文件里CREATE DATABASE cake_shop CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;这种关键语句根本没写你用 MySQL 8.0 默认编码建库中文全变问号。这套资源不一样。它不是教学演示片段也不是半成品骨架而是一个从用户注册→浏览商品→加购→下单→管理员审核→订单查询全链路走通的实战系统。所有模块前台7大功能后台5大管理项均经 IDEA Tomcat 9 MySQL 5.7/8.0 实测运行成功答辩平均分96分——这个分数背后是真实可验证的页面跳转逻辑、无硬编码的数据库连接配置、JSP 中 EL 表达式与 JSTL 标签的规范使用以及每一处// TODO:都已被删掉的干净注释。适合计算机类专业学生快速上手课程设计、教师用于课堂案例拆解、甚至企业新员工做 JavaWeb 入门沙盒环境。别再花3小时配环境今天下午就能把“热销蛋糕”列表刷出来。2. 从零启动环境准备、数据库导入、项目部署三步落地含 IDEA 2023 配置细节2.1 环境版本选型依据为什么坚持用 Tomcat 9 MySQL 5.7/8.0 而非最新版很多新手盲目追求“最新”结果在 IDEA 里新建 JavaWeb 项目时选了 Tomcat 10却发现javax.servlet.*包全部报红——因为 Tomcat 10 已全面迁移到 Jakarta EE 9包名从javax.*改为jakarta.*而本项目所有 JSP、Servlet、Filter 仍基于 Servlet 4.0 规范javax.servlet.http.HttpServlet强行升级会导致编译失败且无意义。同理MySQL 8.0 的caching_sha2_password认证插件与老版 JDBC 驱动如 mysql-connector-java 5.1.47不兼容会卡在Access denied for user。所以本项目实测兼容组合明确锁定为组件推荐版本关键原因JDK1.8u291 或 OpenJDK 8javac -source 1.8编译避免 Lambda 表达式语法报错Tomcat9.0.83非 10.x保持javax.servlet.*包路径不变JSP EL 解析稳定MySQL5.7.42 或 8.0.33需配套驱动5.7 兼容性最好8.0 必须用mysql-connector-java-8.0.33.jarIDEIDEA 2022.3 ~ 2023.3非 2024.x2024 版默认禁用 JSP 编译器需手动开启2023.x 对 web.xml schema 识别最准提示项目lib/目录下已预置mysql-connector-java-5.1.47.jar适配 MySQL 5.7和mysql-connector-java-8.0.33.jar适配 MySQL 8.0部署前请按实际数据库版本只保留一个删掉另一个否则类加载冲突。2.2 数据库初始化不只是执行 SQL而是理解表结构设计意图项目提供cake_shop.sql共 8 张表。不要双击就运行先打开 SQL 文件重点看这三处-- 1. 字符集必须显式声明解决中文乱码核心 CREATE DATABASE IF NOT EXISTS cake_shop CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;-- 2. 用户表 user 的 password 字段长度为 64 —— 这是为 SHA-256 加密预留非明文存储 CREATE TABLE user ( id int(11) NOT NULL AUTO_INCREMENT, username varchar(50) NOT NULL, password varchar(64) NOT NULL, -- 注意64位对应 SHA-256 十六进制字符串长度 realname varchar(20) DEFAULT NULL, phone varchar(15) DEFAULT NULL, address varchar(100) DEFAULT NULL, role tinyint(1) DEFAULT 0, -- 0:普通用户, 1:管理员 PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;-- 3. 订单主表 order_info 的 status 字段用 tinyint 而非 enum —— 便于后期扩展状态机 CREATE TABLE order_info ( id int(11) NOT NULL AUTO_INCREMENT, user_id int(11) NOT NULL, total_price decimal(10,2) NOT NULL, status tinyint(1) NOT NULL DEFAULT 0, -- 0:待支付, 1:已支付, 2:已发货, 3:已完成, 4:已取消 create_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY fk_order_user (user_id), CONSTRAINT fk_order_user FOREIGN KEY (user_id) REFERENCES user (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;执行步骤命令行方式最可控# 1. 登录 MySQL假设 root 密码为 123456 mysql -u root -p123456 # 2. 执行建库建表注意路径替换成你本地实际路径 mysql source /path/to/cake_shop.sql; # 3. 验证数据是否载入关键 mysql SELECT COUNT(*) FROM goods; -- 应返回 12默认12款蛋糕 mysql SELECT username, role FROM user WHERE username admin; -- 应返回 admin, 1注意若用 Navicat 或 DBeaver 执行 SQL请在“运行设置”中勾选“按分号分割语句”和“遇到错误继续执行”否则CREATE DATABASE后的USE cake_shop会因分号缺失而失败。2.3 IDEA 部署全流程从 Import Project 到首页访问含常见 404 排查点Step 1正确导入项目不是“Open”启动 IDEA →File→New→Project from Existing Sources...选择解压后的根目录含src/、WebContent/、lib/、README.md的文件夹在向导中取消勾选 “Create project from external model”即不选 Maven/Gradle因为这是传统 Dynamic Web Project无 pom.xmlStep 2配置 Artifacts最容易被忽略的致命环节File→Project Structure→Artifacts→→Web Application: Archive→For cake-shop在Output Layout标签页确认WEB-INF/lib/下已包含mysql-connector-java-*.jar右键lib/文件夹 →Put into Output RootWEB-INF/web.xml已在WEB-INF/目录下若缺失从WebContent/WEB-INF/拖入index.jsp必须出现在Available Elements顶层右键 →Put into Output RootStep 3配置 Tomcat ServerRun→Edit Configurations...→→Tomcat Server→LocalApplication server: 选择你解压的 Tomcat 9 目录Deployment标签页 →→Artifact→ 选择刚创建的cake-shop:war exploded关键设置Application context必须填/cake-shop不能为空或/否则访问http://localhost:8080/会 404Step 4启动并验证点击绿色三角形启动控制台输出INFO: Server startup in [xxx] milliseconds后浏览器访问http://localhost:8080/cake-shop/→ 应看到首页轮播图http://localhost:8080/cake-shop/admin/login.jsp→ 应看到管理员登录页若首页空白或 404请立即检查web.xml中welcome-file-list是否为welcome-fileindex.jsp/welcome-fileindex.jsp是否在WebContent/根目录而非WebContent/pages/下IDEA 右下角是否弹出Build completed successfully未成功则Build→Rebuild Project3. 前台功能深度拆解从商品展示到购物车结算每一步都带着业务逻辑注释3.1 商品展示模块三层推荐逻辑如何通过 DAO 层解耦实现项目前台首页index.jsp包含三大推荐区条幅推荐Banner、热销推荐、新品推荐。它们并非静态 HTML而是由 Servlet 动态查询数据库生成。核心在于GoodsDAO.java中三个独立方法// src/com/dao/GoodsDAO.java public ListGoods findHotGoods() { String sql SELECT * FROM goods WHERE is_hot 1 ORDER BY sales DESC LIMIT 6; return queryRunner.query(sql, new BeanListHandler(Goods.class)); } public ListGoods findNewGoods() { String sql SELECT * FROM goods WHERE is_new 1 ORDER BY create_time DESC LIMIT 6; return queryRunner.query(sql, new BeanListHandler(Goods.class)); } public ListGoods findBannerGoods() { String sql SELECT * FROM goods WHERE is_banner 1 ORDER BY sort_order ASC; return queryRunner.query(sql, new BeanListHandler(Goods.class)); }参数说明与业务含义is_hot/is_new/is_banner均为TINYINT(1)类型值为1表示启用该推荐标识。管理员可在后台goods_manage.jsp中勾选修改。sales字段记录销量ORDER BY sales DESC确保“热销”按真实销量排序而非点击量或收藏量避免刷单干扰。sort_order是 Banner 专用字段允许管理员拖拽排序如第1位放生日蛋糕第2位放巧克力蛋糕ASC保证序号小的排前面。JSP 层调用逻辑index.jsp片段!-- 条幅推荐区 -- c:forEach items${bannerList} varg varStatusstatus div classitem ${status.index 0 ? active : } img src${pageContext.request.contextPath}/img/${g.image} alt${g.name} div classcarousel-caption h3${g.name}/h3 pfmt:formatNumber value${g.price} typecurrency//p /div /div /c:forEach注意${pageContext.request.contextPath}是关键它动态生成应用上下文路径如/cake-shop避免写死src/img/xxx.jpg导致图片 404。这是新手最常犯的路径错误。3.2 购物车模块Session 存储 vs 数据库存储的取舍与实现细节本项目购物车采用纯 Session 存储非数据库持久化这是课程设计场景下的合理选择✅ 优点无需额外建cart_item表代码简洁符合“轻量级演示”定位❌ 缺点用户关闭浏览器后购物车清空不支持多端同步。核心类Cart.javasrc/com/entity/Cart.java设计public class Cart { private MapInteger, CartItem cartItems; // key: goods_id, value: CartItem private BigDecimal totalAmount; // 实时计算总价避免每次遍历 public void addGoods(Goods goods, int count) { CartItem item cartItems.get(goods.getId()); if (item null) { item new CartItem(goods, count); cartItems.put(goods.getId(), item); } else { item.setCount(item.getCount() count); // 累加数量非覆盖 } recalculateTotal(); // 每次变更后重算总价 } private void recalculateTotal() { totalAmount BigDecimal.ZERO; for (CartItem item : cartItems.values()) { totalAmount totalAmount.add( item.getGoods().getPrice().multiply( BigDecimal.valueOf(item.getCount()) ) ); } } }关键细节MapInteger, CartItem使用goods_id作 key确保同一商品多次添加自动合并如用户先加1个再加2个最终显示数量为3。recalculateTotal()在addGoods()、updateCount()、deleteItem()后强制调用保证totalAmount始终准确避免浮点数累加误差用BigDecimal而非double。CartItem类封装了商品对象和数量不存 price 字段——价格始终从Goods实体获取确保与数据库实时一致若库存售罄价格变动可即时反映。JSP 层购物车展示cart.jspc:forEach items${cart.cartItems} varentry tr tdimg src${pageContext.request.contextPath}/img/${entry.value.goods.image} width60/td td${entry.value.goods.name}/td tdfmt:formatNumber value${entry.value.goods.price} typecurrency//td td form action${pageContext.request.contextPath}/CartServlet methodpost input typehidden namemethod valueupdateCount input typehidden namegoodsId value${entry.value.goods.id} input typenumber namecount value${entry.value.count} min1 stylewidth:60px; button typesubmit更新/button /form /td tdfmt:formatNumber value${entry.value.goods.price.multiply(entry.value.count)} typecurrency//td tda href${pageContext.request.contextPath}/CartServlet?methoddeleteItemgoodsId${entry.value.goods.id}删除/a/td /tr /c:forEach提示表单提交用POST防 URL 长度限制和缓存删除用GET符合 RESTful 习惯且删除操作幂等。CartServlet通过request.getParameter(method)分发到不同处理方法这是典型的 Command 模式简化版。3.3 用户登录与权限控制Filter 如何拦截未登录访问系统区分两类用户普通用户role0和管理员role1。权限控制不在每个 Servlet 里写if(usernull)而是通过LoginFilter.java统一拦截// src/com/filter/LoginFilter.java public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { HttpServletRequest request (HttpServletRequest) req; HttpServletResponse response (HttpServletResponse) resp; // 放行静态资源和登录相关页面 String uri request.getRequestURI(); if (uri.contains(/css/) || uri.contains(/js/) || uri.contains(/img/) || uri.endsWith(login.jsp) || uri.endsWith(register.jsp) || uri.contains(/UserServlet?methodlogin)) { chain.doFilter(req, resp); return; } // 检查 session 中是否存在 user 对象 User user (User) request.getSession().getAttribute(user); if (user null) { response.sendRedirect(request.getContextPath() /login.jsp?msgnot_login); return; } // 管理员专属路径需额外校验 role if (uri.startsWith(/admin/)) { if (user.getRole() ! 1) { response.sendRedirect(request.getContextPath() /index.jsp?msgno_permission); return; } } chain.doFilter(req, resp); }web.xml中 Filter 配置必须放在 Servlet 之前filter filter-nameLoginFilter/filter-name filter-classcom.filter.LoginFilter/filter-class /filter filter-mapping filter-nameLoginFilter/filter-name url-pattern/*/url-pattern !-- 拦截所有请求 -- /filter-mapping避坑点/*拦截所有路径但chain.doFilter()前的return保证了静态资源不被重复处理request.getContextPath()动态获取上下文避免硬编码/cake-shopresponse.sendRedirect()发送 302 重定向比request.getRequestDispatcher().forward()更安全forward 会暴露内部路径。4. 后台管理功能实战从商品增删改查到订单状态流转理解 CRUD 的工程化表达4.1 商品管理分页查询与模糊搜索的 SQL 实现后台商品列表页admin/goods_list.jsp需支持分页显示每页 8 条按名称/描述模糊搜索按分类筛选type_idGoodsDAO.java中对应方法// 分页 搜索 分类筛选三合一 public ListGoods findGoodsByPage(int start, int pageSize, String keyword, Integer typeId) { StringBuilder sql new StringBuilder(SELECT g.*, t.type_name FROM goods g ); sql.append(LEFT JOIN goods_type t ON g.type_id t.id WHERE 11 ); ListObject params new ArrayList(); if (keyword ! null !keyword.trim().isEmpty()) { sql.append(AND (g.name LIKE ? OR g.description LIKE ?) ); params.add(% keyword.trim() %); params.add(% keyword.trim() %); } if (typeId ! null typeId 0) { sql.append(AND g.type_id ? ); params.add(typeId); } sql.append(ORDER BY g.id DESC LIMIT ?, ?); params.add(start); params.add(pageSize); return queryRunner.query(sql.toString(), new BeanListHandler(Goods.class), params.toArray()); } // 获取总记录数用于计算页码 public int getTotalCount(String keyword, Integer typeId) { StringBuilder sql new StringBuilder(SELECT COUNT(*) FROM goods g WHERE 11 ); ListObject params new ArrayList(); if (keyword ! null !keyword.trim().isEmpty()) { sql.append(AND (g.name LIKE ? OR g.description LIKE ?) ); params.add(% keyword.trim() %); params.add(% keyword.trim() %); } if (typeId ! null typeId 0) { sql.append(AND g.type_id ? ); params.add(typeId); } return queryRunner.query(sql.toString(), new ScalarHandlerLong(), params.toArray()).intValue(); }SQL 设计要点使用StringBuilder拼接动态条件避免WHERE 11被误认为低效实际 MySQL 优化器会忽略LIKE ?参数化防止 SQL 注入%keyword%支持前后模糊匹配LEFT JOIN goods_type将分类名称type_name一并查出避免在 JSP 中循环查表N1 问题ORDER BY g.id DESC保证新商品靠前符合运营需求。JSP 分页控件goods_list.jsp!-- 页码导航 -- div classpagination c:if test${currentPage 1} a href${pageContext.request.contextPath}/AdminGoodsServlet?methodlistcurrentPage${currentPage-1}keyword${param.keyword}typeId${param.typeId}laquo;/a /c:if c:forEach begin1 end${totalPage} vari a href${pageContext.request.contextPath}/AdminGoodsServlet?methodlistcurrentPage${i}keyword${param.keyword}typeId${param.typeId} class${i currentPage ? active : }${i}/a /c:forEach c:if test${currentPage totalPage} a href${pageContext.request.contextPath}/AdminGoodsServlet?methodlistcurrentPage${currentPage1}keyword${param.keyword}typeId${param.typeId}raquo;/a /c:if /div注意所有分页链接参数currentPage,keyword,typeId必须原样回传否则筛选条件丢失。这是新手分页失效的头号原因。4.2 订单管理状态机驱动的订单生命周期订单状态流转是电商系统核心。本项目定义 5 种状态0:待支付→1:已支付→2:已发货→3:已完成→4:已取消OrderInfoDAO.java中状态更新方法// 仅允许合法状态转移防越权操作 public boolean updateStatus(int orderId, int newStatus) { // 定义合法转移规则key当前状态, value允许的新状态集合 MapInteger, SetInteger validTransitions new HashMap(); validTransitions.put(0, Set.of(1, 4)); // 待支付 → 可支付或取消 validTransitions.put(1, Set.of(2, 4)); // 已支付 → 可发货或取消 validTransitions.put(2, Set.of(3)); // 已发货 → 只能完成 validTransitions.put(3, Set.of()); // 已完成 → 不可再变 validTransitions.put(4, Set.of()); // 已取消 → 不可再变 // 查询当前状态 String sqlSelect SELECT status FROM order_info WHERE id ?; Integer currentStatus queryRunner.query(sqlSelect, new ScalarHandler(), orderId); if (currentStatus null) return false; SetInteger allowed validTransitions.get(currentStatus); if (allowed null || !allowed.contains(newStatus)) { return false; // 非法转移拒绝执行 } // 执行更新 String sqlUpdate UPDATE order_info SET status ?, update_time NOW() WHERE id ?; return queryRunner.update(sqlUpdate, newStatus, orderId) 0; }后台订单操作admin/order_list.jspc:choose c:when test${order.status 0} a href${pageContext.request.contextPath}/AdminOrderServlet?methodpayid${order.id}标记为已支付/a a href${pageContext.request.contextPath}/AdminOrderServlet?methodcancelid${order.id}取消订单/a /c:when c:when test${order.status 1} a href${pageContext.request.contextPath}/AdminOrderServlet?methodshipid${order.id}发货/a a href${pageContext.request.contextPath}/AdminOrderServlet?methodcancelid${order.id}取消订单/a /c:when c:when test${order.status 2} a href${pageContext.request.contextPath}/AdminOrderServlet?methodcompleteid${order.id}标记完成/a /c:when /c:choose玄学经验状态转移必须在 DAO 层校验不能只靠前端按钮显隐控制F12 可轻易绕过update_time NOW()记录每次状态变更时间为后续数据分析留痕validTransitions用Map而非switch便于未来扩展如增加“退款中”状态。4.3 后台安全加固管理员登录的密码加密与会话超时管理员登录admin/login.jsp提交到AdminUserServlet其login方法关键逻辑// src/com/servlet/AdminUserServlet.java protected void login(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String username request.getParameter(username); String password request.getParameter(password); // 1. 密码 SHA-256 加密非明文比对 String encryptedPassword DigestUtils.sha256Hex(password); // 2. 查询用户role1 限定管理员 User user userDao.findByUsernameAndPassword(username, encryptedPassword); if (user ! null user.getRole() 1) { // 3. 设置 session 并跳转 HttpSession session request.getSession(); session.setAttribute(admin, user); session.setMaxInactiveInterval(1800); // 30分钟无操作自动失效 response.sendRedirect(request.getContextPath() /admin/index.jsp); } else { request.setAttribute(msg, 用户名或密码错误); request.getRequestDispatcher(/admin/login.jsp).forward(request, response); } }密码加密原理DigestUtils.sha256Hex()来自 Apache Commons Codec生成 64 位十六进制字符串数据库user.password字段长度设为 64完美匹配无盐值salt是课程设计简化考虑生产环境必须加盐。会话超时配置双重保障session.setMaxInactiveInterval(1800)在代码中设置web.xml中补充全局配置更可靠session-config session-timeout30/session-timeout !-- 单位分钟 -- /session-config提示session.setAttribute(admin, user)与前台session.setAttribute(user, user)分开存储避免角色混淆。Filter 中user.getRole() 1判断即依赖此分离。5. 避坑指南96分项目背后的5个血泪经验避开这些坑少走3天弯路5.1 现象首页轮播图不显示控制台报 404原因index.jsp中图片路径写成img src/img/banner1.jpg缺少应用上下文路径。解决统一改为img src${pageContext.request.contextPath}/img/banner1.jpg。若用request.getContextPath()需确保web.xml中display-name与部署时 Context Path 一致IDEA 中即Application context值。5.2 现象管理员登录成功后跳转到http://localhost:8080/显示 Tomcat 默认页原因AdminUserServlet中response.sendRedirect(/admin/index.jsp)写成绝对路径/admin/index.jsp未加上下文。解决改为response.sendRedirect(request.getContextPath() /admin/index.jsp)。所有重定向、表单 action、超链接 href 都必须带getContextPath()。5.3 现象商品详情页goods_detail.jsp报NullPointerException错误指向${goods.name}原因GoodsServlet的detail方法中request.setAttribute(goods, goods)后未forward而是redirect导致 request 域数据丢失。解决detail方法必须用request.getRequestDispatcher(/goods_detail.jsp).forward(request, response)不可用sendRedirect。redirect会新建 requestforward才能传递 request 属性。5.4 现象MySQL 8.0 下登录报java.sql.SQLException: Unknown system variable query_cache_size原因项目lib/中混用了mysql-connector-java-5.1.47.jar适配 MySQL 5.7而 MySQL 8.0 已移除查询缓存相关变量。解决删除lib/下所有mysql-connector-java-5.x.jar只保留mysql-connector-java-8.0.33.jar。同时检查db.properties中driverClassName com.mysql.cj.jdbc.Driver8.0 必须用cj版本。5.5 现象购物车更新数量后页面刷新但数量未变原因CartServlet的updateCount方法中request.getSession().setAttribute(cart, cart)后未response.sendRedirect()回购物车页而是forward导致浏览器地址栏仍是CartServletF5 刷新会重复提交。解决updateCount处理完后必须response.sendRedirect(request.getContextPath() /cart.jsp)强制 GET 请求刷新页面避免重复提交PRG 模式。6. 进阶技巧如何用这套代码快速生成你的课程设计报告三个真实可用的文档自动化方案6.1 从源码自动生成 UML 类图PlantUML IDEA 插件一键导出你不需要手动画类图。安装 IDEA 插件PlantUML Integration市场搜索安装然后在项目根目录新建docs/文件夹右键src/com/→PlantUML→Generate Class Diagram选择Goods,User,OrderInfo,CartServlet,GoodsDAO等核心类生成.puml文件内容类似startuml class Goods { int id String name BigDecimal price } class GoodsDAO { ListGoods findHotGoods() } Goods -- GoodsDAO : uses enduml右键.puml文件 →Export Diagrams→ 生成 PNG/PDF直接插入 Word 报告。价值点报告中“系统架构设计”章节的类图不再手绘且与代码实时同步——你改Goods字段重新生成图就更新。6.2 数据库设计说明书用 MySQL Workbench 逆向工程生成 ER 图别再用 Visio 画表关系。用 MySQL Workbench连接你的cake_shop数据库Database→Reverse Engineer...→ 选择cake_shop下一步直到完成自动生成实体关系图ERD右键图表 →Export as PNG在报告“数据库设计”章节粘贴此图并补充说明goods与order_item是一对多一个商品可被多个订单购买user与order_info是一对多一个用户可下多个订单goods_type是分类字典表无业务操作仅供下拉选择。提示Workbench 生成的 ER 图会自动标注外键FK和索引比手绘更专业。6.3 测试用例文档用 JUnit 4 快速编写 3 个核心功能测试课程设计报告要求“系统测试”。不必写黑盒测试步骤用 JUnit 写白盒测试更显功底在src/同级新建test/目录添加GoodsDAOTest.java// test/com/dao/GoodsDAOTest.java public class GoodsDAOTest { private GoodsDAO dao new GoodsDAO(); Test public void testFindHotGoods() { ListGoods list dao.findHotGoods(); assertNotNull(list); assertTrue(list.size() 6); // 热销商品不超过6个 // 验证第一条记录 is_hot1 assertFalse(list.isEmpty()); assertEquals(1, list.get(0).getIsHot().intValue()); } }在 IDEA 中右键该类 →Run GoodsDAOTest控制台输出Tests passed截图测试结果插入报告“测试分析”章节标题“DAO 层单元测试覆盖率 30%核心查询方法”。为什么有效老师一眼看出你懂测试且测试覆盖了业务关键点is_hot标识不是摆设。从那以后我每次交课程设计都强制走一遍这三步用 PlantUML 生成类图5分钟用 Workbench 导出 ER 图3分钟写 3 个 JUnit 测试并截图10分钟。报告质量立刻提升一个档次答辩时老师问“你测试了吗”我能直接打开 IDEA 演示运行过程。不是炫技本文还有配套的精品资源点击获取