ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

JavaWeb大作业实战:JSP+Servlet+MySQL蛋糕店售卖网站完整实现

JavaWeb大作业实战:JSP+Servlet+MySQL蛋糕店售卖网站完整实现 简介这是一份面向计算机相关专业学生与JavaWeb初学者的蛋糕店售卖网站项目源码基于JSP与Servlet技术栈实现可作为课程设计、毕业设计或大作业的完整参考方案。项目覆盖前台核心业务商品分类与推荐展示条幅、热销、新品、商品详情、购物车增删改、用户注册登录、个人信息与收货地址维护、下单付款等模块功能链路较为完整。压缩包共290个文件约18.95MB其中58个jsp页面承载视图层55个java文件实现业务逻辑另有20个xml配置、14个js与12个css负责交互与样式并附带sql脚本、jar依赖及说明文档目录结构清晰便于按模块阅读与二次开发。目前已有182人学习下载。代码经测试可运行适合在此基础上修改扩展功能或用于项目初期立项演示与答辩准备。1. 从一份蛋糕店售卖网站说起JSPServlet 这套老技术栈为什么还值得动手做一遍如果你正在搜「javaweb大作业 蛋糕店售卖网站 源代码」大概率不是想听人讲 MVC 发展史而是手里压着一个必须交的东西一个能跑起来的蛋糕店售卖网站带 JSP 页面、Servlet 后端、MySQL 数据库最好还有一份能照着复现的文档说明。这个标题拆开看就是三件事——用 JSP 做视图、用 Servlet 做控制、用 MySQL 存商品和订单最后拼成一个能下单、能看订单、能管商品的完整闭环。它适合两类人一类是刚学完 Servlet 生命周期、还没独立串过一个完整项目的新手需要一份能跑通的最小案例另一类是工作几年、突然被要求带学生或做技术验证的老手想快速确认这套老栈在 IDEA Tomcat 下到底怎么配才不翻车。蛋糕店只是个业务外壳真正要练的是请求怎么从浏览器进到 Servlet、数据怎么从 MySQL 回到 JSP、会话怎么在多次请求之间保持住。把这套跑通换成书店、花店、奶茶店代码结构几乎不用大改。2. 蛋糕店售卖网站的技术选型JSP、Servlet 和 MySQL 各自负责什么2.1 为什么这个作业绕不开 JSP Servlet MySQL 三件套先把这个项目的骨架说清楚。JSP 负责把后端数据渲染成 HTMLServlet 负责接收请求、调业务、转发页面MySQL 负责持久化商品、用户、订单。三者之间靠HttpServletRequest、HttpServletResponse、HttpSession这几个对象串起来。很多同学一上来就纠结「JSP 是不是过时了」但大作业的评分点往往不在技术新旧而在你能不能把请求链路走通、把数据存对、把页面跳转理清楚。选型上有一个现实约束JSP 和 Servlet 是 Java Web 课程的标准内容Tomcat 对它们的支持最直接不需要额外引入 Spring 那一套注解和依赖注入。对于一份要交的作业来说依赖越少环境越容易复现。MySQL 选 5.7 或 8.0 都行关键是驱动包版本要和数据库对上后面避坑章节会专门讲这个。从分层角度看常见做法是entity放实体类dao放数据库操作service放业务逻辑servlet放请求处理jsp放页面。这个分层不是必须的但强烈建议保留因为一旦所有代码都塞进 Servlet后面改一个字段要翻十几个文件血泪经验。2.2 项目目录结构和依赖怎么摆在 IDEA 里新建一个 Java Enterprise 项目或者手动建一个 Web 项目目录结构大致如下cake-shop/ ├── src/ │ └── main/ │ ├── java/ │ │ └── com/cake/ │ │ ├── entity/ # 实体类 │ │ ├── dao/ # 数据库访问 │ │ ├── service/ # 业务逻辑 │ │ ├── servlet/ # 控制器 │ │ └── util/ # 数据库连接工具 │ ├── resources/ │ │ └── db.properties # 数据库配置 │ └── webapp/ │ ├── WEB-INF/ │ │ └── web.xml # 部署描述符 │ ├── css/ │ ├── images/ │ └── *.jsp # 页面 └── pom.xml 或 lib/ # 依赖依赖只需要三样javax.servlet-apiTomcat 提供scope 设 provided、mysql-connector-java、jstl页面里用标签库时。如果用 Mavenpom.xml里加对应坐标如果不用 Maven就把 jar 包丢进WEB-INF/lib。这里有个细节Servlet API 的 jar 不要打进 war 包否则和 Tomcat 自带的冲突启动就报ClassNotFoundException或NoSuchMethodError。数据库连接建议抽一个工具类用db.properties存 URL、用户名、密码避免硬编码。连接池在这个规模的项目里不是必须的但如果你想让代码看起来更专业可以引入 Druid 或 HikariCP不过要额外加依赖作业场景下用DriverManager就够了。3. 用 IDEA 跑通第一个 Servlet环境配置和最小可运行案例3.1 Tomcat 配置和第一个能返回内容的 Servlet环境配置是新手翻车最多的地方。IDEA 里配置 Tomcat 的步骤是Run → Edit Configurations → 加号 → Tomcat Server → Local → 在 Server 选项卡里指定 Tomcat 安装目录 → 在 Deployment 选项卡里添加 Artifact选 war exploded。这里选 exploded 而不是 war是因为 exploded 支持热更新改 JSP 不用重启。配好之后写第一个 Servlet 验证链路package com.cake.servlet; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; // 用注解方式注册访问路径为 /hello WebServlet(/hello) public class HelloServlet extends HttpServlet { Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 设置响应类型和编码防止中文乱码 resp.setContentType(text/html;charsetUTF-8); // 往请求域里放数据供 JSP 读取 req.setAttribute(msg, 蛋糕店首页加载成功); // 转发到 JSP 页面注意路径以 / 开头 req.getRequestDispatcher(/hello.jsp).forward(req, resp); } }这段代码的逻辑是浏览器访问/helloTomcat 根据WebServlet注解找到这个类调用doGet把数据放进 request 域然后转发给hello.jsp。参数说明setContentType里的charsetUTF-8必须写否则中文会变成问号getRequestDispatcher的路径是相对于 webapp 根目录的写错就报 404。对应的hello.jsp里用 EL 表达式取值% page contentTypetext/html;charsetUTF-8 languagejava % html headtitle蛋糕店/title/head body h1${msg}/h1 /body /html启动 Tomcat浏览器访问http://localhost:8080/你的项目名/hello看到「蛋糕店首页加载成功」就说明链路通了。这一步看着简单但它是后面所有功能的基础如果这里不通先别往下写。3.2 数据库连接和商品列表查询的最小实现链路通了之后接数据库。先在 MySQL 里建库建表CREATE DATABASE cake_shop DEFAULT CHARACTER SET utf8mb4; USE cake_shop; CREATE TABLE product ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(100) NOT NULL, price DECIMAL(10,2) NOT NULL, image VARCHAR(255), description TEXT ); INSERT INTO product (name, price, image, description) VALUES (草莓奶油蛋糕, 128.00, strawberry.jpg, 新鲜草莓配动物奶油), (巧克力慕斯, 158.00, chocolate.jpg, 比利时黑巧制作);然后写数据库工具类package com.cake.util; import java.io.InputStream; import java.sql.Connection; import java.sql.DriverManager; import java.util.Properties; public class DBUtil { private static String url; private static String username; private static String password; // 静态块在类加载时执行一次读取配置文件 static { try (InputStream in DBUtil.class.getClassLoader() .getResourceAsStream(db.properties)) { Properties props new Properties(); props.load(in); url props.getProperty(jdbc.url); username props.getProperty(jdbc.username); password props.getProperty(jdbc.password); // 加载驱动MySQL 8 用 com.mysql.cj.jdbc.Driver Class.forName(props.getProperty(jdbc.driver)); } catch (Exception e) { throw new RuntimeException(数据库配置加载失败, e); } } public static Connection getConnection() throws Exception { return DriverManager.getConnection(url, username, password); } }db.properties内容jdbc.drivercom.mysql.cj.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/cake_shop?useSSLfalseserverTimezoneAsia/ShanghaicharacterEncodingutf8 jdbc.usernameroot jdbc.password你的密码参数说明serverTimezone不写会报时区错误characterEncodingutf8保证中文不乱码useSSLfalse在本地开发时避免证书警告。MySQL 5.7 的驱动类名是com.mysql.jdbc.Driver8.0 是com.mysql.cj.jdbc.Driver写错直接启动失败。有了工具类写一个查询商品列表的 ServletWebServlet(/productList) public class ProductListServlet extends HttpServlet { Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { ListProduct list new ArrayList(); String sql SELECT id, name, price, image, description FROM product; 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.setImage(rs.getString(image)); p.setDescription(rs.getString(description)); list.add(p); } } catch (Exception e) { e.printStackTrace(); } req.setAttribute(products, list); req.getRequestDispatcher(/productList.jsp).forward(req, resp); } }这里用try-with-resources自动关闭连接避免忘记关连接导致连接数耗尽。PreparedStatement比Statement安全能防 SQL 注入这是作业里容易被忽略但老师会看的点。4. 蛋糕店核心功能落地登录、购物车和订单提交怎么写4.1 用户登录和 Session 会话保持登录功能的核心是 Session。用户提交用户名密码Servlet 校验通过后把用户对象放进HttpSession后续请求从 Session 里取用户信息判断是否登录。WebServlet(/login) public class LoginServlet extends HttpServlet { Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding(UTF-8); String username req.getParameter(username); String password req.getParameter(password); UserDao userDao new UserDao(); User user userDao.findByUsernameAndPassword(username, password); if (user ! null) { // 登录成功把用户放进 session HttpSession session req.getSession(); session.setAttribute(user, user); resp.sendRedirect(req.getContextPath() /productList); } else { req.setAttribute(error, 用户名或密码错误); req.getRequestDispatcher(/login.jsp).forward(req, resp); } } }参数说明req.setCharacterEncoding(UTF-8)必须在取参数之前调用否则 POST 中文乱码sendRedirect是重定向地址栏会变适合登录成功后跳转forward是转发地址栏不变适合带错误信息回页面。这两个混用是新手常见错误后面避坑章节会展开。4.2 购物车用 Session 还是数据库购物车有两种实现存 Session 或存数据库。作业场景下推荐存 Session因为不用建表、不用处理并发逻辑简单。用一个MapInteger, CartItem存在 Session 里key 是商品 idvalue 是数量和商品信息。WebServlet(/addToCart) public class AddToCartServlet extends HttpServlet { Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { int productId Integer.parseInt(req.getParameter(productId)); HttpSession session req.getSession(); // 从 session 取购物车没有就新建 MapInteger, CartItem cart (MapInteger, CartItem) session.getAttribute(cart); if (cart null) { cart new HashMap(); } CartItem item cart.get(productId); if (item null) { Product product new ProductDao().findById(productId); item new CartItem(product, 1); } else { item.setQuantity(item.getQuantity() 1); } cart.put(productId, item); session.setAttribute(cart, cart); resp.sendRedirect(req.getContextPath() /cart.jsp); } }这里有个类型转换警告session.getAttribute返回 Object强转成 Map 时编译器会提示 unchecked加SuppressWarnings(unchecked)即可。购物车存 Session 的缺点是换浏览器或清缓存就丢但对作业来说够用。4.3 订单提交和事务处理订单提交要同时写订单主表和订单明细表必须用事务保证一致性。如果订单写进去了但明细没写数据就脏了。public boolean createOrder(int userId, MapInteger, CartItem cart) { String orderSql INSERT INTO orders (user_id, total_price, create_time) VALUES (?, ?, NOW()); String itemSql INSERT INTO order_item (order_id, product_id, quantity, price) VALUES (?, ?, ?, ?); Connection conn null; try { conn DBUtil.getConnection(); // 关闭自动提交开启事务 conn.setAutoCommit(false); PreparedStatement orderPs conn.prepareStatement(orderSql, Statement.RETURN_GENERATED_KEYS); orderPs.setInt(1, userId); orderPs.setBigDecimal(2, calculateTotal(cart)); orderPs.executeUpdate(); // 拿到刚插入的订单 id ResultSet keys orderPs.getGeneratedKeys(); int orderId 0; if (keys.next()) { orderId keys.getInt(1); } PreparedStatement itemPs conn.prepareStatement(itemSql); for (CartItem item : cart.values()) { itemPs.setInt(1, orderId); itemPs.setInt(2, item.getProduct().getId()); itemPs.setInt(3, item.getQuantity()); itemPs.setBigDecimal(4, item.getProduct().getPrice()); itemPs.addBatch(); } itemPs.executeBatch(); conn.commit(); return true; } catch (Exception e) { if (conn ! null) { try { conn.rollback(); } catch (Exception ex) { ex.printStackTrace(); } } e.printStackTrace(); return false; } finally { if (conn ! null) { try { conn.setAutoCommit(true); conn.close(); } catch (Exception e) { e.printStackTrace(); } } } }关键点setAutoCommit(false)开启事务commit()提交异常时rollback()回滚。Statement.RETURN_GENERATED_KEYS用来拿自增主键这是订单和明细关联的关键。批量插入用addBatchexecuteBatch比循环单条插入快很多。5. 避坑与排查JSPServlet 项目最容易翻车的 5 个地方5.1 中文乱码现象、原因和三种解决方式现象页面显示问号或乱码表单提交后数据库里存的是乱码。原因请求和响应的编码不一致或者数据库连接没指定字符集。解决第一JSP 页面顶部加% page contentTypetext/html;charsetUTF-8 %第二Servlet 里req.setCharacterEncoding(UTF-8)和resp.setContentType(text/html;charsetUTF-8)都要写第三数据库 URL 加characterEncodingutf8建库时用utf8mb4。三个地方缺一个都可能乱码这是最典型的玄学问题。5.2 404 和 500路径写错和空指针怎么定位现象访问页面报 404 或 500。原因404 通常是WebServlet路径和访问地址对不上或者 JSP 放在WEB-INF下直接访问500 多半是空指针比如session.getAttribute取出来是 null 没判断就强转。解决404 先看 Tomcat 启动日志里 Servlet 有没有注册成功再看访问路径有没有带项目名500 看控制台堆栈定位到具体行号重点检查从 Session 和 request 里取出来的对象有没有判空。5.3 数据库驱动版本不匹配ClassNotFoundException 的排查顺序现象启动报ClassNotFoundException: com.mysql.jdbc.Driver或com.mysql.cj.jdbc.Driver。原因驱动类名和 MySQL 版本不对应或者 jar 包没放进WEB-INF/lib。解决MySQL 5.7 用com.mysql.jdbc.Driver8.0 用com.mysql.cj.jdbc.Driver确认 jar 包在WEB-INF/lib下而不是项目根目录如果用 Maven确认 scope 不是 provided。排查顺序是先看版本再看 jar 位置最后看类名。5.4 forward 和 redirect 混用导致的重复提交现象刷新页面后订单重复提交或者转发后页面样式丢失。原因表单提交后用 forward浏览器地址栏还是提交前的地址刷新就重新提交一次。解决表单处理成功后用sendRedirect重定向到结果页这是 PRGPost-Redirect-Get模式。forward 只用在需要带数据回显的场景比如校验失败回表单页。这个坑在订单模块特别致命重复下单就是数据事故。5.5 Session 失效和购物车丢失现象用户登录后过一会操作购物车空了或者提示未登录。原因Session 默认 30 分钟过期或者 Tomcat 重启导致 Session 丢失。解决在web.xml里配置session-configsession-timeout60/session-timeout/session-config延长过期时间开发阶段 Tomcat 重启丢 Session 是正常的可以改用数据库存购物车但作业场景下没必要。另外注意session.invalidate()是注销别在错误的地方调用。6. 让这份大作业更耐看分页查询和代码组织的几个实用技巧分页是蛋糕店商品列表的加分项。不做分页商品一多页面就卡做了分页代码结构也更像真实项目。核心是 SQL 的LIMIT和OFFSET配合一个计算总页数的查询。public ListProduct findByPage(int pageNum, int pageSize) { ListProduct list new ArrayList(); // LIMIT 第一个参数是偏移量第二个是每页条数 String sql SELECT * FROM product LIMIT ? OFFSET ?; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setInt(1, pageSize); ps.setInt(2, (pageNum - 1) * pageSize); 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)); list.add(p); } } catch (Exception e) { e.printStackTrace(); } return list; }参数说明pageNum从 1 开始pageSize一般设 8 或 12OFFSET等于(pageNum - 1) * pageSize。总页数用SELECT COUNT(*) FROM product除以pageSize向上取整。JSP 页面里用 JSTL 的c:forEach渲染页码当前页高亮。代码组织上我一般会把所有 SQL 集中在 DAO 层Servlet 里不出现 SQL 字符串。这样改表结构时只动 DAO不用满项目搜 SQL。另外实体类的字段名和数据库列名保持一致能省掉很多映射的麻烦。如果时间充裕可以加一个过滤器统一处理编码WebFilter(/*) public class EncodingFilter implements Filter { Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { req.setCharacterEncoding(UTF-8); resp.setContentType(text/html;charsetUTF-8); chain.doFilter(req, resp); } }这样每个 Servlet 里就不用重复写编码设置了代码干净不少。最后说一个我自己的习惯每写完一个功能就 commit 一次哪怕没有 Git 服务器本地建个仓库也行。大作业改到后面经常出现「昨天还能跑今天就不行了」有版本记录能省下大量后悔药。这套 JSP Servlet 的东西看着老但把请求链路、会话管理、事务处理这几块吃透后面学任何框架都会觉得似曾相识。希望帮到你。本文还有配套的精品资源点击获取
返回列表