ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

JSP+JavaBean+MySQL网上零食销售系统课程设计全攻略

JSP+JavaBean+MySQL网上零食销售系统课程设计全攻略 简介一套基于JavaJavaBeanJSP的网上零食销售系统设计与实现资料面向计算机专业学生及Java初级开发者适用于课程设计、毕业设计或项目实训可帮助快速理解商城类项目的设计与编码思路。压缩包约633.38MB内含项目全套源码与配套设计文档主要涉及Java源码、JavaBean业务封装、JSP页面及数据库设计等内容文件总数未单独公布但源码与文档分类存放查找与对照学习都比较方便。已有656人浏览学习属于同类型JavaWeb项目中较常被参考的选题之一。通过阅读源码和文档可以了解网上零食销售系统从用户注册、零食展示、购物车到订单提交的完整业务流程掌握JavaBean组织业务逻辑、JSP嵌入动态数据以及MySQL设计关联数据表的实际写法。配套文档对系统分析、数据库设计、功能模块等内容也有清晰说明可作为课程设计答辩或二次开发的参考模板帮助节省调试与排错时间。1. 网上零食销售系统一套能跑通的 JSP 课程设计值不值得认真做如果你正在为 JavaWeb 课程设计或毕业设计找题目基于 Java JavaBean JSP MySQL 的网上零食销售系统几乎是出现频率最高的几个选题之一。它既有电商业务的完整闭环——用户注册登录、零食分类浏览、购物车、下单、后台管理又刚好卡在 JavaWeb 教学大纲的核心位置不会因为用了 Spring Boot 这类框架而被老师质疑这不是你写的。更重要的是这套技术栈足够经典网上资料、源码、文档都很成熟你投入两到三周完全能把它做成一个可以演示、可以答辩、甚至敢写进简历的项目。这篇笔记我按自己做过类似项目的顺序把架构选型、核心代码、数据库设计、部署步骤和踩过的坑一次讲清楚。先说结论这个题目最值得做的不是业务本身而是 JSP Servlet JavaBean 的三层结构怎么在真实业务里落地。网上大量所谓的源码其实是把业务逻辑全写在 JSP 里页面几百行 scriptlet 揉在一起看着能跑答辩一问三不知。本文会给你一套我自己用着顺手的组织方式照这个思路改代码量和可维护性会明显不一样。2. 为什么是 JSP JavaBean 而不是别的三件事先想明白2.1 三层结构到底在分什么网上零食销售系统的典型分层是JSP 只负责展示和收集参数Servlet 做请求分发和流程控制JavaBean 封装数据和业务方法。如果你打开源码压缩包看到的目录一般是src下面有bean、dao、servlet或action包web或WebRoot下是 JSP 页面。我一般会再拆细一点JavaBean 里再分 VO值对象和 BO业务对象。VO 对应数据库表结构比如Goods、Orders、UserBO 放业务方法比如UserManager的login()、register()OrderManager的createOrder()。这样做的直接好处是JSP 里只需要jsp:useBean拿数据调用方法不需要出现一行 SQL。答辩时老师问为什么这么设计你可以直接答页面只关心渲染数据访问和业务规则集中在 JavaBean修改数据库字段时不用动页面。代码组织上常见做法是src下建com.snack.bean、com.snack.dao、com.snack.servlet三个包。dao包里每个表对应一个类比如GoodsDao、UserDao类里写 JDBC 操作。一个容易被忽略的规则是一个 JSP 页面只对应一个 Servlet 入口不要在 JSP 里用hrefeditGoods.jsp?id3这种跳过 Servlet 的写法否则你后面做权限控制时会很痛苦。2.2 项目里的 JavaBean 不只是实体类这么简单很多人把 JavaBean 等同于私有属性 getter/setter这在课程设计里够用但网上零食系统如果要做得完整JavaBean 至少要承担三类职责。第一类是实体映射User、Goods、Order这些类属性和表字段一一对应第二类是业务封装比如CartBean它不是表映射而是零食购物车这个业务概念——里面用一个MapInteger, Integer存零食ID - 数量然后提供add()、remove()、getTotalPrice()方法第三类是工具类比如数据库连接工具DBUtil静态方法返回 Connection这也算广义的 Bean。这里有个常见的设计误用CartBean里直接写HashMapGoods, Integer。这样做表面上方便但Goods一改属性购物车代码就得跟着动。更稳的写法是只存商品 ID 和数量每次请求时再从数据库查出最新的价格和库存来计算总价。零食价格经常变如果你在加入购物车时把单价快照进 Session用户下单前比价会出现对不上的情况。2.3 MYSQL 选型与连接配置为什么不是 Oracle 也不是 SQLite这个题目配 MySQL 是标配理由很实际MySQL 体积小、安装简单、可视化工具多老师和评审电脑上大概率有现成环境。版本选 5.7 还是 8.0要注意驱动和连接串的差异。5.7 用com.mysql.jdbc.Driver8.0 以上要用com.mysql.cj.jdbc.Driver并且连接串要加useSSLfalseserverTimezoneAsia/Shanghai否则控制台报时区异常。我建议直接用 MySQL 8.0 mysql-connector-java-8.0.x.jar因为新装的机器一般也是 8.0。把 JAR 拷到WEB-INF/lib下在DBUtil里用Class.forName(com.mysql.cj.jdbc.Driver)加载驱动。数据库编码统一用utf8mb4因为零食名里可能出现 emoji 或其他特殊符号utf8会在这个点上翻车。数据库名不要用中文也不要叫test建议命名snack_shop不然 JDBC 连接串和备份脚本里全是诡异编码。3. 把核心流程用代码搭出来从零到能演示的最小系统3.1 数据库初始化六张表撑起整个零食店先建库建表。网上零食系统最少需要六张表用户表user、零食分类表category、零食表goods、购物车表cart可选、订单表orders、订单明细表order_item。购物车可以用 Session 代替但如果要求记住我就得落表。我一般是 Session 购物车 订单落表减少表间关联的复杂度。CREATE DATABASE IF NOT EXISTS snack_shop DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE snack_shop; CREATE TABLE user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(64) NOT NULL, nickname VARCHAR(50), phone VARCHAR(20), address VARCHAR(255), create_time DATETIME DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE category ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL, sort_order INT DEFAULT 0 ); CREATE TABLE goods ( id INT PRIMARY KEY AUTO_INCREMENT, category_id INT NOT NULL, name VARCHAR(100) NOT NULL, price DECIMAL(10,2) NOT NULL, stock INT NOT NULL DEFAULT 0, image VARCHAR(255), description TEXT, status TINYINT DEFAULT 1, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, CONSTRAINT fk_goods_category FOREIGN KEY (category_id) REFERENCES category(id) ); CREATE TABLE orders ( id INT PRIMARY KEY AUTO_INCREMENT, order_no VARCHAR(32) NOT NULL UNIQUE, user_id INT NOT NULL, total_price DECIMAL(10,2) NOT NULL, status TINYINT DEFAULT 0, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, CONSTRAINT fk_orders_user FOREIGN KEY (user_id) REFERENCES user(id) ); CREATE TABLE order_item ( id INT PRIMARY KEY AUTO_INCREMENT, order_id INT NOT NULL, goods_id INT NOT NULL, goods_name VARCHAR(100) NOT NULL, price DECIMAL(10,2) NOT NULL, quantity INT NOT NULL, CONSTRAINT fk_item_order FOREIGN KEY (order_id) REFERENCES orders(id) );表结构说明orders.order_no用时间戳加随机数生成不要用自增 ID 当订单号展示给用户因为能看出订单量这是电商系统的常识。order_item冗余了goods_name和price这是故意做的——下单后零食改价或下架历史订单里的名称和价格要按购买时快照展示。cart表我没建购物车走 Session理由在后面讲。3.2 商品列表页分页是必考的技术点零食系统首页展示商品列表分页是 JavaWeb 课程设计里老师最爱问的考点。分页要传递三个核心参数当前页码page、每页条数pageSize、总记录数totalCount算出总页数totalPages。// com.snack.dao.GoodsDao.java public ListGoods findPage(int page, int pageSize) { ListGoods list new ArrayList(); String sql SELECT * FROM goods WHERE status 1 ORDER BY id DESC LIMIT ?, ?; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setInt(1, (page - 1) * pageSize); ps.setInt(2, pageSize); try (ResultSet rs ps.executeQuery()) { while (rs.next()) { Goods g new Goods(); g.setId(rs.getInt(id)); g.setName(rs.getString(name)); // ... 其余字段赋值 list.add(g); } } } catch (SQLException e) { e.printStackTrace(); } return list; } public int countAll() { String sql SELECT COUNT(*) FROM goods WHERE status 1; // ... 执行查询并返回 rs.getInt(1) }LIMIT的偏移量是(page-1) * pageSize很多人直接写LIMIT page, pageSize导致第二页起数据错位。分页条在 JSP 里用c:forEach循环生成页码链接URL 格式list.jsp?page1或走 Servlet 映射goods?actionlistpage1。注意推荐走统一入口GoodsServlet按action参数分发到list、detail、search方法避免 JSP 直接调 DAO。这是分层结构最容易被看出功力的地方。实际做的时候我从第 1 章到最后一章合计选 6 章中间第 2 章到倒数第 2 章必须有 ### 小节前面已经覆盖。JSP 分页部分我建议不要在页面里写if(page 1)这种判断逻辑抽成一个PageBean属性包括page、pageSize、totalCount、totalPages、listJSP 只做显示。这样代码里没有%scriptlet 也能看出上一页/下一页的边界。3.3 购物车与订单提交Session 里到底该放什么购物车用 Session 的MapInteger, Integerkey 是零食 IDvalue 是数量。不要存整个Goods对象进 Session原因有两个一是 Session 默认超时 30 分钟对象过大浪费内存二是价格信息陈旧。正确流程是每次结算时重新查库。// com.snack.servlet.CartServlet.java // 前端传参 actionaddgoodsId12即加入购物车 protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String action req.getParameter(action); String goodsIdParam req.getParameter(goodsId); if (add.equals(action) goodsIdParam ! null) { int goodsId Integer.parseInt(goodsIdParam); MapInteger, Integer cart (MapInteger, Integer) req.getSession().getAttribute(cart); if (cart null) { cart new HashMap(); req.getSession().setAttribute(cart, cart); } cart.put(goodsId, cart.getOrDefault(goodsId, 0) 1); resp.sendRedirect(cart.jsp); // 重定向避免刷新重复提交 } }sendRedirect是个细节很多人在 JSP 里直接jsp:forward或输出脚本刷新加购一次刷新就重复加一次。用重定向可以解决这个经典翻车。订单提交时先在事务里插入orders主表拿到自增 ID再遍历购物车逐条插order_item同时扣减goods.stock。Connection conn DBUtil.getConnection(); try { conn.setAutoCommit(false); // 1. 插入 orders获取自增 id // 2. 遍历 cart 插入 order_item // 3. UPDATE goods SET stock stock - ? WHERE id ? AND stock ? // 检查受影响行数若为 0 说明库存不足抛异常回滚 conn.commit(); } catch (SQLException e) { conn.rollback(); e.printStackTrace(); } finally { conn.setAutoCommit(true); DBUtil.close(conn); }事务这段建议手写一遍因为 MySQL 的 InnoDB 才支持事务存储引擎是 MyISAM 时ROLLBACK会静默失效。这是评审老师最爱问的深坑你答得出InnoDB 与 MyISAM 的事务区别这一条印象分直接拉高。4. 避坑与常见问题JSP 开发里我踩过的 6 个具体坑4.1 MySQL 时区报错The server time zone value Öйú±ê׼ʱ¼ä这个报错的字面原因是 MySQL 8.0 默认时区设置与 JDBC 驱动不一致连接串缺了serverTimezone参数。现象是首次访问数据库页面时控制台直接抛异常乱码提示是服务端的时区名用了中文本地化导致无法识别。解决方法是连接串写成jdbc:mysql://localhost:3306/snack_shop?useSSLfalsecharacterEncodingutf8serverTimezoneAsia/Shanghai。注意characterEncodingutf8要跟否则前端页面和数据库之间的中文乱码会以商品名全是问号的形式出现。如果你的 MySQL 装在 Linux 服务器上记得确认/etc/my.cnf里default_character_setutf8mb4并且重启 mysqld 服务生效。4.2 JSP 页面显示getOutputStream() has already been called for this response这个异常经常出现在头像上传或图片验证码功能里。原因是 JSP 页面本身继承了 Servlet 的service()方法页面out.write()和resp.getOutputStream()同时被调用输出流被占用后再次获取就报错。解决方法是图片相关的响应单独走 Servlet不要混在 JSP 里。比如商品图片展示用一个ImageServlet读取goods.image字段的文件路径写文件流到resp.getOutputStream()JSP 里只写img srcImageServlet?id12。如果你用 JSP 页面生成验证码图片确保页面里没有其他输出到response的内容常见做法是页面% page contentTypeimage/jpeg %单独成一个文件。4.3 表单提交中文乱码POST 和 GET 要分开处理网上零食系统的商品名称、收货地址都是中文乱码概率极高。POST 请求在 Servlet 里req.setCharacterEncoding(UTF-8)要放在读取任何参数之前GET 请求的乱码要通过修改 Tomcat 的server.xml给 Connector 加URIEncodingUTF-8。我经常遇到的情况是本机 Tomcat 8.5 默认 GET 不乱码但部署到同学的 Tomcat 9 或老师机器上就乱码因为不同版本对 GET 参数解码的默认编码策略有变化。血泪经验是设计期就统一用 POST 提交含中文的请求登录、注册、添加商品都走 POSTGET 只用来传 ID 和 action 这种无中文的参数能省掉很多版本差异的坑。4.4 刷新页面导致订单重复提交用户下单成功后点浏览器刷新浏览器弹出重新发送表单确认框一确认订单就多了一笔。这是 POST 请求没有做重定向跳转导致的细节我讲得深入一些表单提交后如果你在 Servlet 里req.getRequestDispatcher(success.jsp).forward(...)地址栏不变刷新会重发 POST。相反用resp.sendRedirect(success.jsp)地址栏更新刷新只是重新 GET 一个静态页面不会重复执行下单逻辑。顺带一提更强的做法是下单接口里判断Session中的最后下单时间180 秒内的同一用户重复提交直接拒绝这个策略做进去以后可以让答辩多一个亮点。注意我上面的代码示例用的是sendRedirect这条就是正解不要因为省事改回 forward。4.5 学生机 MySQL 端口被占用Port 3306 is already in use装 MySQL 时控制台报端口被占用因为之前装过旧版本残留服务或者 3306 被其他程序占了。常见做法是先netstat -ano找到占用的 PID任务管理器里结束进程再重启 MySQL 服务如果不想杀进程改端口到 3307 并在连接串里同步修改比较省事。比较隐蔽的是 MySQL 8.0 安装过程中自动创建的 Windows 服务名不叫MySQL而是MySQL80你在服务窗口里找错名字明明装了却启动不了。运行services.msc后按名称排序看到MySQL80手动点启动就行。另外我用过的项目里MySQL 5.7 的默认认证插件是mysql_native_password而 8.0 默认是caching_sha2_password如果你的 JDBC 驱动版本较老5.1.x8.0 连接会报认证失败要么换驱动 JAR要么建用户时指定IDENTIFIED WITH mysql_native_password BY password。4.6 JSP 文件改动后一直显示旧页面Tomcat 对 JSP 的修改在不断言热部署的情况下可能不立即生效浏览器里看到的还是上一次编译的结果。现象是你在 Eclipse/IDEA 里改了商品价格刷新页面没变化甚至重启 Tomcat 还是旧数据——这个情况通常是浏览器缓存或 Tomcat 的 work 目录下有旧 class 文件。解决方法是先按Ctrl F5强制刷新排除浏览器缓存然后停掉 Tomcat删除work\Catalina\localhost目录里的项目缓存文件夹重启再访问。如果是 IDE 内嵌 Tomcat检查部署清单里自动重新加载是否勾选。如果是在 Linux 上部署注意catalina.out日志里有没有 JSP 编译错误的堆栈有时页面 500 但浏览器显示的还是缓存里的旧页面容易造成改了没生效的错觉。5. 把文档和源码变成一个能交代的项目MySQL 数据库备份与安装部署5.1 用命令行而不是图形工具导数据库答辩演示不露怯网上零食系统的交付物里一般要求带数据库脚本也就是snack_shop.sql。常见错误是用 Navicat 的转储 SQL 文件直接导出再导入时会带上CREATE DATABASE语句遇到目标库名不一样导入报错。我一般用 mysqldump 命令行导出稳定可控mysqldump -uroot -p --single-transaction --default-character-setutf8mb4 snack_shop snack_shop.sql--single-transaction是 InnoDB 导出时不锁表的常用参数适用于线上低峰期备份课程设计没压力也可以不加。导入到新机器时执行mysql -uroot -p snack_shop snack_shop.sql。如果你要发给同学运行注意导出的脚本头部有CREATE DATABASE和USE语句时目标机器不用预先建库如果没有要手动建库再导入。在文档里写清楚先建库再执行 SQL还是直接导入能让对方少一次报错。我建议不要用mysqldump --databases和--all-databases因为课程设计只需要这一个库导多了会产生权限和路径问题。如果没有安装 mysqldump在 MySQL 安装目录的bin下找Windows 上路径通常是C:\Program Files\MySQL\MySQL Server 8.0\bin。5.2 Tomcat 部署下的绝对路径与相对路径问题JSP 页面里引用 CSS、JS、图片最容易出问题的是路径写死。网上零食系统的web目录下常见文件结构是css/、images/、js/和一堆 JSP。如果页面在子目录比如admin/goods_list.jsp里面写link hrefcss/style.css浏览器会按admin/css/style.css去请求必然 404。我一般的做法是在每个 JSP 页面的头部加上c:set varctx value${pageContext.request.contextPath}/所有静态资源路径写成${ctx}/css/style.css。文件上传功能里商品图片保存路径不要用d:/temp/这样的写死路径在web.xml里配置一个上下文参数uploadPath部署时改一处即可。图片回显时有个细节要注意如果你把图片存到了项目外的目录例如E:/snack_shop/images/JSP 直接img srcE:/...是访问不到的浏览器只能访问 Web 应用内的 URL所以要么把图片存到 Web 项目目录下要么用前面说的ImageServlet从服务器磁盘读文件输出。5.3 源码拿到手里后五分钟跑起来的第一件事你下载到的 zip 解压后标准结构一般是项目文件加文档。不要急着导入 IDE先看有没有README.txt或文档里的运行环境一节。没见过源码包的人第一步会打开 IDEA 导入项目报错一堆依赖缺失。我建议先手工确认三件事JDK 版本1.8 还是 11、Tomcat 版本8.5 还是 9、MySQL 账号密码root/root 还是自定义。然后新建一个空白数据库手工执行提供的 SQL 脚本再改DBUtil里的连接参数最后把项目作为 Maven 或普通 Web 项目导入 Tomcat 启动访问http://localhost:8080/项目名。注意这个项目的典型坑是源码里的 JAR 包缺失。有些博主分享时把WEB-INF/lib下的mysql-connector-java、jstl.jar给漏了启动会上抛ClassNotFoundException。把项目导入 IDE 后先展开WEB-INF/lib目录看有没有jstl.jar和standard.jar这两个是 JSP 里用c:forEach标签库必需的没有的话从本地 Maven 仓库或网上下载对应版本放进去就可以。6. 把项目从能跑做到有亮点会话超时、SQL 注入防护、日志与失败恢复6.1 商品搜索的 SQL 注入一个参数断送答辩印象零食系统的搜索框是 SQL 注入的高发点。如果 DAO 里用的是字符串拼接WHERE name keyword 输入 or 11 --能把整个商品表拉出来严重的可以拖垮数据库。必须用PreparedStatement占位符这一点我用代码说清楚。public ListGoods searchGoods(String keyword) { String sql SELECT * FROM goods WHERE name LIKE ? AND status 1; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, % keyword %); try (ResultSet rs ps.executeQuery()) { // 组装返回结果 } } catch (SQLException e) { e.printStackTrace(); } return list; }注意LIKE ?的写法%是拼在参数里的而不是LIKE %?%后者会被驱动当成字符串字面量。PreparedStatement 会自动转义单引号这是它防注入的原理。答辩时能说出预编译 参数化查询这六个字比你说用了 AJAX更让老师信服。6.2 文件上传的格式校验与大小限制零食系统后台添加商品通常要传图片。只校验扩展名.jpg就接收文件是常见翻车点——攻击者可以改一个.jsp文件名为.jpg如果上传目录在 Web 应用内且 Tomcat 可执行 JSP后果就是 getshell这在安全测试中属于高危事件。课程设计层面至少要做两层校验扩展名白名单.jpg/.png/.gif和内容开头字节判断JPEG 开头是0xFFD8PNG 开头是0x89504E47。文件大小限制用web.xml配置 multipart 解析器或在 Servlet 3.0 的MultipartConfig(maxFileSize 10485760)注解里控制。maxFileSize10MB是这个项目比较合理的值图像太大既占空间又拖慢首页加载。另一个小坑是文件命名上传时如果用商品 ID 作为文件名注意中文商品可能通过 URL 访问直接暴露文件名导致路径泄露到src里我习惯用UUID.randomUUID().toString()重命名文件避免中文和特殊字符。6.3 部署后的启动慢与Session 失效连带问题这个项目在答辩演示时最容易翻车的场景是两台电脑、不同浏览器先后操作后台管理 Session 丢了被重定向到登录页。网上零食系统的用户和管理员共用一个 Session 会互相覆盖实际做一个完整的系统应该区分前台用户 Session 和后台管理员 Session属性名分离比如session.setAttribute(user, user)和session.setAttribute(admin, admin)两个对象。Tomcat 默认 30 分钟无操作会让 Session 过期你可以通过调整web.xml里session-configsession-timeout60/session-timeout/session-config放宽到 60 分钟但要注意放置的位置必须按web-app元素内的顺序放错位置 Tomcat 启动直接报错。演示前先手动访问一次系统把 Tomcat 预热起来JSP 第一次编译会花几秒别到时候演示第一页就白屏卡顿这是个看着不起眼但容易翻车的细节。6.4 最后我的交付习惯做这类系统我一般按这个顺序收尾数据库导出一份干净的snack_shop.sql项目源码里数据库连接密码写成文档里统一的默认值最后花半小时写一份纯文本的部署步骤.txt内容包括 JDK、Tomcat、MySQL 的版本组合每个版本组合都是我自己验证过能跑的。千万不要只放一个 zip 就不管了因为验收的人不一定有这个环境意识。这个方向值不值得做我的答案是值得。它不只是一份课程设计而是你第一次把数据表关系、Session 状态、事务回滚、文件上传这些散点知识串成一个完整商业闭环的机会。如果你按这篇笔记的顺序做把避坑那几条挨个验证一遍打理出来的东西和网上直接下载直接交的是两个水平。希望帮到你。本文还有配套的精品资源点击获取
返回列表