首页 / 资讯中心 / 文章详情

JavaWeb蛋糕店售卖网站源码拆解:JSP+Servlet+MySQL完整落地

JavaWeb蛋糕店售卖网站源码拆解:JSP+Servlet+MySQL完整落地 ★ FEATURED ARTICLE
简介这是一套基于JSP与Servlet技术栈实现的JavaWeb蛋糕店售卖网站完整源码面向正在学习JavaWeb开发、需要课程设计或毕业设计参考的初学者与进阶开发者。项目采用IDEA 2017.3.5开发运行于Tomcat 8.5.35数据库为MySQL 5.7并引入c3p0连接池与DBUtil工具类技术选型贴近企业常见实践。前台涵盖商品推荐与分类展示、详情查看、购物车增删改、用户注册登录、个人信息与收货地址维护、下单付款、订单查询及关键字搜索后台支持订单状态流转、用户管理、商品类目与商品信息的增删改查功能链路完整。资源包共290个文件包含58个jsp页面、55个java源码、20个xml配置、14个js脚本及css、图片等静态资源另附1个sql建库脚本整体约19.11MB目录结构清晰便于按模块阅读与二次开发。目前已有127人学习适合作为JavaWeb综合实战与功能扩展的参考模板。1. 蛋糕店售卖网站源码拆解从 JSPServlet 到 MySQL 的完整落地路径很多同学做 JavaWeb 课设或毕业设计时一看到“蛋糕店售卖网站”这种题目就头大——前台要展示商品、加购物车、下单后台还要管订单、管用户、管类目功能一多就不知道从哪下手。这份基于 JSP 和 Servlet 的 JavaWeb 源码包恰好把这一整套电商核心链路跑通了。它用 Bootstrap 做前端布局MySQL 5.7 存数据c3p0 连接池加 DBUtils 操作数据库开发环境是 IDEA 2017.3.5 配 Tomcat 8.5.35。适合正在找 JavaWeb 完整案例 MySQL 实战项目的同学也适合想拿它改造成自己毕设的开发者。下面我按“能跑起来、能看懂、能改”的顺序把这份资源拆开讲。2. 环境搭建与数据库初始化IDEA 2017.3.5 Tomcat 8.5.35 MySQL 5.72.1 开发环境版本对齐与 JDK 配置这份源码对版本比较敏感不是随便换个 Tomcat 9 或 MySQL 8 就能直接跑的。先说你最容易翻车的地方JDK 版本。源码用的是 JDK 8如果你本机装了多个 JDKIDEA 里一定要把 Project SDK 和 Module SDK 都指到 1.8。常见做法是在File → Project Structure → Project里设置 Language level 为 8然后在Modules → Sources里确认同样为 8。Tomcat 8.5.35 在 IDEA 里的配置路径是Run → Edit Configurations → → Tomcat Server → Local在 Deployment 标签页添加 ArtifactApplication context 建议设为/cake这样访问路径就是http://localhost:8080/cake/。MySQL 5.7 的安装注意字符集选utf8mb4排序规则用utf8mb4_general_ci。如果你用 MySQL 8驱动类要从com.mysql.jdbc.Driver改成com.mysql.cj.jdbc.Driver连接串还要加serverTimezoneAsia/Shanghai否则会报时区错误。这是第一个血泪经验版本不对后面全是玄学报错。2.2 数据库建表与 c3p0 连接池配置源码里数据库操作依赖 c3p0 和 DBUtils所以c3p0-config.xml必须放在src根目录下不能放到WEB-INF里。一个典型的配置长这样c3p0-config default-config property namedriverClasscom.mysql.jdbc.Driver/property property namejdbcUrljdbc:mysql://localhost:3306/cake_shop?useUnicodetrueamp;characterEncodingutf8/property property nameuserroot/property property namepassword你的密码/property property nameinitialPoolSize5/property property namemaxPoolSize20/property property namecheckoutTimeout3000/property /default-config /c3p0-configinitialPoolSize是初始连接数本地开发 5 就够maxPoolSize是最大连接数课设演示 20 足够设太大反而拖慢启动。checkoutTimeout是获取连接的超时时间单位毫秒3000 表示 3 秒拿不到连接就抛异常。数据库名要和jdbcUrl里保持一致建库语句用CREATE DATABASE cake_shop DEFAULT CHARACTER SET utf8mb4;。建表 SQL 一般在源码的sql文件夹里导入时先选库再执行否则会报“No database selected”。2.3 项目导入与 Tomcat 部署验证导入项目后先检查WEB-INF/lib下有没有 c3p0、DBUtils、mysql-connector 这三个 jar。如果没有需要手动加到 Artifact 的WEB-INF/lib里光在 IDEA 的 Libraries 里加是不够的部署时会丢。启动 Tomcat 后控制台如果出现 c3p0 的初始化日志说明连接池加载成功。访问首页能看到蛋糕商品列表就说明 JSP 编译和 Servlet 映射都正常。如果首页 404先看web.xml里welcome-file是不是index.jsp再看 Artifact 的 Output Layout 里有没有把 JSP 文件包含进去。3. 前台核心链路商品展示、购物车与订单提交3.1 商品多维度展示的 Servlet 转发逻辑前台首页的商品展示分三类条幅推荐、热销推荐、新品推荐再加上按类型展示。这些数据不是写死在 JSP 里的而是通过 Servlet 查库后setAttribute再转发到 JSP。典型流程是IndexServlet调用 Service 层Service 调用 DAO 层DAO 用 DBUtils 的QueryRunner执行 SQL把结果集映射成Product实体列表。JSP 页面用 JSTL 的c:forEach遍历输出。// IndexServlet.java 核心片段 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ProductService productService new ProductService(); // 条幅推荐按推荐类型查 ListProduct bannerList productService.getProductsByType(banner); // 热销推荐按销量倒序取前 8 条 ListProduct hotList productService.getHotProducts(8); // 新品推荐按上架时间倒序取前 8 条 ListProduct newList productService.getNewProducts(8); request.setAttribute(bannerList, bannerList); request.setAttribute(hotList, hotList); request.setAttribute(newList, newList); request.getRequestDispatcher(/index.jsp).forward(request, response); }这里的关键参数是getHotProducts(8)里的 8表示取前 8 条你可以改成 4 或 12 来适配页面布局。getProductsByType(banner)里的banner是数据库里的推荐类型字段值改的时候要同步改数据库。注意转发用forward而不是redirect因为 request 域里的数据要带到 JSP重定向会丢数据。3.2 购物车数据结构与数量修改实现购物车是这份源码里比较有代表性的部分。它没有用 Redis 或 Session 存整个购物车对象而是把购物车项存成MapInteger, CartItemkey 是商品 IDvalue 是包含商品信息和数量的CartItem。这样修改数量时直接根据商品 ID 定位效率比 List 遍历高。加入购物车的 Servlet 逻辑是先判断 Session 里有没有 cart没有就 new 一个然后判断商品是否已在 cart 里在就数量加一不在就新增一项。// CartServlet.java 加入购物车 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { int productId Integer.parseInt(request.getParameter(pid)); HttpSession session request.getSession(); MapInteger, CartItem cart (MapInteger, CartItem) session.getAttribute(cart); if (cart null) { cart new HashMap(); session.setAttribute(cart, cart); } if (cart.containsKey(productId)) { CartItem item cart.get(productId); item.setCount(item.getCount() 1); } else { Product product productService.getProductById(productId); CartItem item new CartItem(); item.setProduct(product); item.setCount(1); cart.put(productId, item); } response.sendRedirect(request.getContextPath() /cart.jsp); }pid是前端传过来的商品 ID用Integer.parseInt转换时如果参数为空会抛NumberFormatException所以前端链接里必须带pid。修改数量的逻辑类似只是把setCount改成前端传过来的目标数量。这里有个坑Session 里的购物车在用户关闭浏览器后默认 30 分钟失效如果你希望持久化得在用户登录后把购物车写回数据库。3.3 订单提交与用户订单查询付款流程是购物车页面点击“去结算”先校验用户是否登录没登录就跳登录页登录后进入订单确认页填写收货信息提交后生成订单主表和订单明细表。订单主表存订单号、用户 ID、总金额、收货人、电话、地址、状态订单明细表存订单号、商品 ID、数量、单价。生成订单时要用事务保证主表和明细表同时成功或同时失败。// OrderService.java 生成订单 public void createOrder(Order order, ListOrderItem itemList) { Connection conn null; try { conn C3P0Utils.getConnection(); conn.setAutoCommit(false); orderDao.insertOrder(conn, order); for (OrderItem item : itemList) { orderDao.insertOrderItem(conn, item); } conn.commit(); } catch (SQLException e) { try { conn.rollback(); } catch (SQLException ex) { ex.printStackTrace(); } throw new RuntimeException(下单失败); } finally { try { conn.setAutoCommit(true); conn.close(); } catch (SQLException e) { e.printStackTrace(); } } }setAutoCommit(false)是关闭自动提交手动控制事务。conn.commit()提交conn.rollback()回滚。注意 DBUtils 的QueryRunner默认是自动提交所以这里要自己拿 Connection 来管事务。用户订单查询就是按用户 ID 查订单主表再根据订单号查明细表在 JSP 里嵌套遍历展示。4. 后台管理模块订单、用户、类目与商品操作4.1 管理员权限拦截与后台入口后台功能不是所有登录用户都能进的。源码的做法是用户表里有个role字段0 表示普通用户1 表示管理员。登录成功后把role存到 SessionJSP 页面用c:if test${sessionScope.user.role 1}判断是否显示后台管理按钮。但光隐藏按钮不够直接访问后台 URL 还是能进所以需要一个权限过滤器。// AdminFilter.java public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { HttpServletRequest request (HttpServletRequest) req; HttpServletResponse response (HttpServletResponse) resp; User user (User) request.getSession().getAttribute(user); if (user null || user.getRole() ! 1) { response.sendRedirect(request.getContextPath() /login.jsp); return; } chain.doFilter(req, resp); }过滤器在web.xml里配置拦截/admin/*路径。user.getRole() ! 1这个判断是核心少了它普通用户就能越权操作。常见错误是过滤器配了但没生效检查web.xml里url-pattern是不是写成了/admin而不是/admin/*。4.2 订单状态流转与用户管理后台订单操作包括按状态查询、发货、完成、删除。订单状态一般用数字表示1 待付款、2 已付款待发货、3 已发货、4 已完成。发货操作就是把状态从 2 改成 3完成就是从 3 改成 4。删除订单要同时删主表和明细表否则会留下孤儿数据。-- 按状态查询订单 SELECT * FROM orders WHERE status ? ORDER BY create_time DESC; -- 修改订单状态 UPDATE orders SET status ? WHERE order_id ?; -- 删除订单先删明细再删主表 DELETE FROM order_item WHERE order_id ?; DELETE FROM orders WHERE order_id ?;用户管理包括查询所有用户、新增用户、修改密码、修改信息和删除。新增用户时密码要做 MD5 加密再存源码里如果没加密建议你加上这是基本的安全习惯。删除用户前要先检查该用户有没有未完成订单有的话不能直接删否则订单表的外键会出问题。4.3 商品类目与商品 CRUD 的关联约束商品类目和商品是一对多关系一个类目下可以有多个商品。删除类目时如果该类目下还有商品直接删会报外键约束错误。正确做法是先查该类目下有没有商品有就提示“请先移除该类目下的商品”没有才执行删除。// CategoryService.java 删除类目 public boolean deleteCategory(int categoryId) { int productCount productDao.countByCategoryId(categoryId); if (productCount 0) { return false; // 有商品不允许删除 } return categoryDao.deleteById(categoryId) 0; }商品新增时要选类目修改时可以改类目。商品图片上传是另一个容易出问题的地方源码里如果用的是相对路径部署后图片可能找不到。建议把上传目录配成绝对路径或者在 Tomcat 里配虚拟路径。商品删除同样要先检查有没有被订单引用有的话只能下架不能删除。5. 避坑与排查版本、编码、连接池与路径问题5.1 中文乱码从请求到响应的三层排查现象表单提交中文变成问号或乱码。原因通常有三层请求编码、响应编码、数据库编码。解决顺序是先在 Servlet 里加request.setCharacterEncoding(UTF-8)再设response.setContentType(text/html;charsetUTF-8)然后检查数据库连接串有没有characterEncodingutf8最后确认表和字段的字符集是utf8mb4。如果是 GET 请求乱码还要在 Tomcat 的server.xml里给 Connector 加URIEncodingUTF-8。5.2 c3p0 连接池报错驱动类找不到与连接泄漏现象启动时报ClassNotFoundException: com.mysql.jdbc.Driver。原因是 mysql-connector jar 没放到WEB-INF/lib下。解决方法是把 jar 复制到该目录并在 IDEA 的 Artifact 里确认已包含。另一个常见现象是跑一段时间后报“Too many connections”原因是 DAO 里拿了 Connection 没关。用 DBUtils 的QueryRunner一般不用手动关但自己写事务的地方一定要在 finally 里 close。5.3 JSP 页面路径错误相对路径与绝对路径混用现象CSS 和 JS 加载不出来页面样式全乱。原因是 JSP 里用了相对路径而 Servlet 转发后浏览器地址栏还是原地址相对路径就变了。解决办法是统一用${pageContext.request.contextPath}拼绝对路径比如link relstylesheet href${pageContext.request.contextPath}/css/bootstrap.css。这样不管怎么转发路径都不会错。5.4 订单重复提交刷新页面导致重复下单现象用户提交订单后按 F5 刷新又生成一笔订单。原因是表单提交后直接转发到了成功页刷新时浏览器重复提交了上一次的 POST 请求。解决办法是提交后用response.sendRedirect重定向到订单列表页而不是forward。重定向会让浏览器发起新的 GET 请求刷新就不会重复提交。5.5 Tomcat 启动失败端口占用与 JDK 版本不匹配现象启动 Tomcat 报Address already in use: JVM_Bind。原因是 8080 端口被占用了。解决办法是改 Tomcat 的conf/server.xml里的 Connector 端口或者在 IDEA 的 Run Configuration 里改 HTTP port。另一个现象是报Unsupported major.minor version这是 JDK 版本不匹配编译用的 JDK 比运行的高统一改成 JDK 8 即可。6. 二次开发与进阶技巧从课设到可演示项目如果你想让这个项目在答辩时更出彩有几个方向可以改。第一是加一个简单的分页商品列表和订单列表都加上用LIMIT和OFFSET实现前端用 Bootstrap 的分页组件。第二是把密码存储从明文改成 MD5 加盐登录时先查盐再校验这是安全加分项。第三是给订单状态加一个时间轴展示用户能直观看到“已下单→已发货→已完成”的流转。// 分页查询示例 public ListProduct getProductsByPage(int page, int pageSize) { int offset (page - 1) * pageSize; String sql SELECT * FROM product LIMIT ? OFFSET ?; try { return queryRunner.query(sql, new BeanListHandler(Product.class), pageSize, offset); } catch (SQLException e) { throw new RuntimeException(e); } }page是当前页码从 1 开始pageSize是每页条数建议 8 或 12offset是偏移量公式是(page - 1) * pageSize。这个分页逻辑可以直接复用到订单列表和用户列表。还有一个技巧是给项目加一个全局异常处理页。在web.xml里配error-page把 404 和 500 都指向一个友好的错误页答辩时如果现场演示出 bug至少不会看到一堆堆栈信息。我一般还会在web.xml里配一个session-timeout默认 30 分钟演示时改成 60 分钟避免讲到一半 Session 过期。最后说一个我自己的习惯每次改完 DAO 层的 SQL我都会先在 MySQL 客户端里把 SQL 跑一遍确认结果对了再写进 Java 代码。因为 Java 里报的 SQL 异常往往只给一个SQLException具体哪错了还得靠数据库客户端来定位。从那以后我每次调 SQL 都强制走一遍“客户端验证→代码集成→页面联调”的流程省了很多来回折腾的时间。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站