ARTICLE DETAIL

资讯详情

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

JSP购物系统毕业设计实战:环境搭建、数据库导入与核心模块实现

JSP购物系统毕业设计实战:环境搭建、数据库导入与核心模块实现 简介这份毕业设计资源面向计算机相关专业学生与Java Web初学者提供一套基于JSP的网上购物系统完整实现方案覆盖从需求分析、数据库设计到编码部署的全流程。包内包含项目报告、答辩PPT、源代码、数据库脚本、运行截图及部署视频压缩包约56.59MB文件以源码、文档、演示素材为主便于对照学习与二次开发。系统采用JSP与Servlet协同处理请求与展示结合JDBC访问数据库并体现MVC分层思想涉及商品展示、购物车、用户登录、订单管理等核心模块同时兼顾界面布局与基础安全考量。目前已有1216人学习下载适合需要完成课程设计或毕业设计的学生参考也可作为Java Web入门练手项目帮助理解Web应用从开发到部署的完整链路。1. 从一份 JSP 购物系统压缩包说起毕业设计到底交付什么很多计算机专业的同学在选题阶段会搜到「基于jsp的网上购物系统毕业设计」下载下来发现是一个压缩包里面塞着项目报告、答辩PPT、源代码、数据库脚本和部署视频。表面上看东西很全但真正打开之后十个人里有八个会卡在同一个地方Tomcat 起不来数据库连不上页面 404然后开始怀疑这份代码是不是「假的」。我带过几届学生的课程设计也帮人排查过不少这类项目实际情况是——代码本身大概率能跑问题出在环境版本和配置路径上。JSP 购物系统作为毕业设计选题核心价值不在于技术有多新而在于它覆盖了 Java Web 开发最完整的一条链路前端页面、Servlet 控制层、JDBC 数据访问、MySQL 数据库设计、会话管理、文件上传。这套东西放到今天依然是从业者理解 Web 应用骨架的经典路径。这篇文章面向的是拿到这类压缩包但不知道怎么让它跑起来、或者想自己从零搭一套购物系统的同学我会把环境搭建、数据库导入、核心模块实现、部署排错这几个环节拆开讲清楚让你能真正把这份毕业设计变成自己能讲明白的东西。2. 环境选型与项目结构为什么 JDK 8 Tomcat 8 是底线2.1 版本搭配的硬约束JSP 购物系统这类项目绝大多数是在 JDK 8 时代写的用的 Servlet 规范是 3.1 或 4.0Tomcat 版本对应 8.5 或 9.0。如果你用 JDK 17 去跑一个基于javax.servlet的老项目会遇到包名找不到的问题——因为 JDK 9 之后模块化改革javax.servlet被移到了jakarta.servlet而老项目的 import 语句全是javax开头。这不是代码写错了是版本不匹配。我一般建议的搭配是组件推荐版本说明JDK1.8.0_301兼容性最好避免模块化问题Tomcat8.5.78支持 Servlet 3.1稳定MySQL5.7 或 8.0注意驱动包版本要对应IDEEclipse 或 IDEA看项目原始配置数据库工具Navicat 或 dbx 数据库工具导入 SQL 脚本方便这里重点说 MySQL 驱动的坑。MySQL 5.7 用mysql-connector-java-5.1.x.jarMySQL 8.0 用mysql-connector-java-8.0.x.jar两者的连接 URL 写法不同。5.7 的 URL 是jdbc:mysql://localhost:3306/数据库名8.0 必须加时区和 SSL 参数jdbc:mysql://localhost:3306/数据库名?useSSLfalseserverTimezoneAsia/ShanghaicharacterEncodingutf8。很多同学导入项目后报「Communications link failure」八成就是这里没改。2.2 项目目录结构怎么读拿到源代码后先别急着往 IDE 里导。用文件管理器打开看清楚目录结构。典型的 JSP 购物系统长这样ShoppingSystem/ ├── src/ # Java 源码 │ └── com/shop/ │ ├── dao/ # 数据访问层 │ ├── servlet/ # 控制层 │ ├── entity/ # 实体类 │ └── util/ # 工具类数据库连接等 ├── WebContent/ 或 web/ # Web 资源 │ ├── css/ │ ├── js/ │ ├── images/ │ ├── WEB-INF/ │ │ ├── web.xml # 部署描述符 │ │ └── lib/ # 第三方 jar 包 │ └── *.jsp # 页面文件 └── database/ └── shop.sql # 数据库脚本关键要看WEB-INF/lib下面有没有mysql-connector-java.jar如果没有需要自己放进去。另外src/com/shop/util下面通常有一个DBUtil.java或DBHelper.java里面写着数据库连接的用户名和密码导入数据库后要改成你自己的。2.3 导入项目的操作步骤以 Eclipse 为例操作流程如下第一步新建一个 Dynamic Web Project项目名和压缩包里的保持一致Target runtime 选 Tomcat 8.5。第二步把src下的 Java 包整个复制到新项目的src目录把WebContent下的内容复制到新项目的WebContent。第三步右键项目 → Build Path → Configure Build Path → Libraries确认 JRE 是 1.8然后 Add External JARs把WEB-INF/lib下所有 jar 加进去。第四步打开DBUtil.java修改数据库连接信息// DBUtil.java 数据库连接工具类 public class DBUtil { // 数据库驱动MySQL 5.7 用 com.mysql.jdbc.Driver // MySQL 8.0 用 com.mysql.cj.jdbc.Driver private static final String DRIVER com.mysql.cj.jdbc.Driver; // 连接地址8.0 必须带时区参数 private static final String URL jdbc:mysql://localhost:3306/shop_db ?useSSLfalseserverTimezoneAsia/ShanghaicharacterEncodingutf8; // 改成你自己的 MySQL 用户名和密码 private static final String USER root; private static final String PASSWORD 你的密码; public static Connection getConnection() throws Exception { Class.forName(DRIVER); return DriverManager.getConnection(URL, USER, PASSWORD); } }这段代码的逻辑很直白加载驱动类然后通过DriverManager获取连接。参数说明——DRIVER根据 MySQL 版本二选一URL里的shop_db要和你实际创建的数据库名一致USER和PASSWORD填本机 MySQL 的账号。如果连接报错先检查 MySQL 服务有没有启动再检查端口是不是 3306最后确认用户名密码。提示如果项目用的是连接池比如 Druid 或 C3P0配置文件通常在src下的druid.properties或c3p0-config.xml改那里的 URL 和密码不要改 DBUtil。3. 数据库导入与表结构核对别让外键把你卡住3.1 导入 SQL 脚本的正确姿势压缩包里一般有一个shop.sql或database.sql里面是建库建表语句加初始数据。导入方式有两种命令行和图形化工具。命令行方式# 先登录 MySQL mysql -u root -p # 创建数据库如果脚本里没有 CREATE DATABASE 语句 CREATE DATABASE shop_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; # 切换到该数据库 USE shop_db; # 导入 SQL 文件注意路径用正斜杠或双反斜杠 source D:/graduation/shop.sql;如果用 dbx 数据库工具或 Navicat操作更简单新建连接 → 右键「运行 SQL 文件」→ 选择脚本 → 执行。但要注意编码问题如果脚本是 GBK 编码而你的工具默认 UTF-8中文会变乱码。解决办法是在导入前用记事本打开 SQL 文件另存为 UTF-8 格式。3.2 核心表结构长什么样购物系统的数据库设计万变不离其宗核心表就那么几张表名作用关键字段user用户信息id, username, password, phone, addressproduct商品信息id, name, price, stock, image, category_idcategory商品分类id, name, parent_idcart购物车id, user_id, product_id, quantityorders订单主表id, user_id, total_price, status, create_timeorder_item订单明细id, order_id, product_id, quantity, price导入之后用一条 SQL 验证数据是否完整-- 检查各表数据量确认导入成功 SELECT user AS table_name, COUNT(*) AS row_count FROM user UNION ALL SELECT product, COUNT(*) FROM product UNION ALL SELECT category, COUNT(*) FROM category; -- 检查外键关联是否正常比如购物车里的商品是否存在 SELECT c.id, c.user_id, p.name AS product_name, c.quantity FROM cart c LEFT JOIN product p ON c.product_id p.id WHERE p.id IS NULL;第一条 SQL 是看每张表有没有数据如果 product 表是 0 条说明初始数据没导进去。第二条 SQL 是查孤儿记录——购物车里引用了不存在的商品这种情况会导致页面报空指针。如果查出来有结果说明外键约束没生效或者数据有问题需要手动清理。3.3 数据库增删改查在项目里怎么体现购物系统的每个功能背后都是增删改查。以商品管理为例DAO 层通常长这样// ProductDao.java 商品数据访问层 public class ProductDao { // 查询所有商品支持按分类筛选 public ListProduct findAll(int categoryId) throws Exception { ListProduct list new ArrayList(); String sql SELECT * FROM product WHERE 11; if (categoryId 0) { sql AND category_id ?; } sql ORDER BY id DESC; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { if (categoryId 0) { ps.setInt(1, categoryId); } 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.setStock(rs.getInt(stock)); p.setImage(rs.getString(image)); list.add(p); } } return list; } // 根据 ID 查询单个商品用于详情页 public Product findById(int id) throws Exception { String sql SELECT * FROM product WHERE id ?; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setInt(1, id); ResultSet rs ps.executeQuery(); if (rs.next()) { Product p new Product(); p.setId(rs.getInt(id)); p.setName(rs.getString(name)); p.setPrice(rs.getBigDecimal(price)); p.setStock(rs.getInt(stock)); p.setImage(rs.getString(image)); return p; } } return null; } }这段代码用了PreparedStatement而不是Statement目的是防止 SQL 注入——比如用户在搜索框输入 OR 11用拼接字符串的方式就会把整张表查出来。try-with-resources语法确保连接和语句自动关闭避免连接泄漏。参数说明categoryId传 0 表示查全部传具体值表示按分类过滤。findById返回 null 表示商品不存在Servlet 层要判断这个 null否则详情页会报 500。4. 核心功能模块实现从登录到下单的完整链路4.1 登录与 Session 管理登录功能是购物系统的入口实现逻辑是用户提交用户名密码 → Servlet 调用 DAO 查询 → 匹配成功则把用户对象存入 Session → 跳转首页。核心代码如下// LoginServlet.java 登录控制器 WebServlet(/login) public class LoginServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 设置编码防止中文乱码 request.setCharacterEncoding(UTF-8); response.setContentType(text/html;charsetUTF-8); String username request.getParameter(username); String password request.getParameter(password); UserDao userDao new UserDao(); try { User user userDao.login(username, password); if (user ! null) { // 登录成功把用户信息存入 Session HttpSession session request.getSession(); session.setAttribute(currentUser, user); // 重定向到首页 response.sendRedirect(request.getContextPath() /index.jsp); } else { // 登录失败带回错误信息 request.setAttribute(msg, 用户名或密码错误); request.getRequestDispatcher(/login.jsp).forward(request, response); } } catch (Exception e) { e.printStackTrace(); response.sendRedirect(request.getContextPath() /error.jsp); } } }逻辑说明request.getSession()会获取当前会话如果没有就新建一个。setAttribute把 user 对象存进去后续页面用session.getAttribute(currentUser)就能拿到。注意sendRedirect和forward的区别——重定向是让浏览器重新发请求URL 会变转发是服务器内部跳转URL 不变。登录成功用重定向是为了防止用户刷新页面重复提交表单。参数说明username和password从表单的 input name 获取必须和 JSP 页面里的 name 属性一致。如果登录后跳转 404检查request.getContextPath()返回的项目路径是否正确。4.2 购物车与订单生成购物车的实现有两种思路存 Session 或存数据库。存 Session 简单但关浏览器就丢存数据库持久但要多写几张表。毕业设计里两种都常见我一般建议存数据库因为答辩时老师更认可这种方案。加入购物车的 Servlet 逻辑// AddCartServlet.java 加入购物车 WebServlet(/addCart) public class AddCartServlet extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { HttpSession session request.getSession(); User user (User) session.getAttribute(currentUser); // 未登录则跳转登录页 if (user null) { response.sendRedirect(request.getContextPath() /login.jsp); return; } int productId Integer.parseInt(request.getParameter(productId)); int quantity Integer.parseInt(request.getParameter(quantity)); CartDao cartDao new CartDao(); try { // 先查购物车里是否已有该商品 Cart exist cartDao.findByUserAndProduct(user.getId(), productId); if (exist ! null) { // 已有则累加数量 cartDao.updateQuantity(exist.getId(), exist.getQuantity() quantity); } else { // 没有则新增记录 Cart cart new Cart(); cart.setUserId(user.getId()); cart.setProductId(productId); cart.setQuantity(quantity); cartDao.insert(cart); } response.sendRedirect(request.getContextPath() /cart.jsp); } catch (Exception e) { e.printStackTrace(); response.sendRedirect(request.getContextPath() /error.jsp); } } }这段代码的关键在于「先查后插」——如果购物车里已经有这个商品就更新数量而不是重复插入。findByUserAndProduct的 SQL 是SELECT * FROM cart WHERE user_id ? AND product_id ?。参数说明productId从商品列表页的链接传过来quantity从数量输入框获取默认是 1。订单生成涉及事务处理因为要同时往orders和order_item两张表插入数据还要扣减库存。核心逻辑// OrderService.java 订单业务逻辑 public class OrderService { public boolean createOrder(int userId, ListCart cartList) throws Exception { Connection conn null; try { conn DBUtil.getConnection(); // 开启事务关闭自动提交 conn.setAutoCommit(false); // 计算总价 BigDecimal total BigDecimal.ZERO; for (Cart cart : cartList) { Product p new ProductDao().findById(cart.getProductId()); total total.add(p.getPrice().multiply(new BigDecimal(cart.getQuantity()))); } // 插入订单主表 String orderSql INSERT INTO orders(user_id, total_price, status, create_time) VALUES(?,?,?,NOW()); PreparedStatement ps conn.prepareStatement(orderSql, Statement.RETURN_GENERATED_KEYS); ps.setInt(1, userId); ps.setBigDecimal(2, total); ps.setInt(3, 0); // 0 表示待付款 ps.executeUpdate(); // 获取自动生成的订单 ID ResultSet rs ps.getGeneratedKeys(); int orderId 0; if (rs.next()) { orderId rs.getInt(1); } // 插入订单明细并扣减库存 for (Cart cart : cartList) { String itemSql INSERT INTO order_item(order_id, product_id, quantity, price) VALUES(?,?,?,?); PreparedStatement ps2 conn.prepareStatement(itemSql); ps2.setInt(1, orderId); ps2.setInt(2, cart.getProductId()); ps2.setInt(3, cart.getQuantity()); ps2.setBigDecimal(4, new ProductDao().findById(cart.getProductId()).getPrice()); ps2.executeUpdate(); // 扣减库存 String stockSql UPDATE product SET stock stock - ? WHERE id ? AND stock ?; PreparedStatement ps3 conn.prepareStatement(stockSql); ps3.setInt(1, cart.getQuantity()); ps3.setInt(2, cart.getProductId()); ps3.setInt(3, cart.getQuantity()); int affected ps3.executeUpdate(); if (affected 0) { // 库存不足回滚事务 conn.rollback(); return false; } } // 清空购物车 String clearSql DELETE FROM cart WHERE user_id ?; PreparedStatement ps4 conn.prepareStatement(clearSql); ps4.setInt(1, userId); ps4.executeUpdate(); // 提交事务 conn.commit(); return true; } catch (Exception e) { if (conn ! null) { conn.rollback(); } throw e; } finally { if (conn ! null) { conn.setAutoCommit(true); conn.close(); } } } }这段代码是购物系统里最复杂的部分核心是事务控制。setAutoCommit(false)开启事务后所有 SQL 要么全部成功要么全部回滚。扣库存时用了stock ?条件如果返回影响行数为 0说明库存不够立即回滚。参数说明Statement.RETURN_GENERATED_KEYS让 JDBC 返回自增主键用于关联订单明细。status字段 0 表示待付款1 表示已付款2 表示已发货具体含义看项目文档。4.3 商品图片上传与展示购物系统的商品图片处理是个容易被忽略的环节。常见做法是把图片存在WebContent/upload目录下数据库只存文件名。上传用commons-fileupload组件// UploadServlet.java 图片上传 WebServlet(/upload) MultipartConfig(maxFileSize 1024 * 1024 * 5) // 限制 5MB public class UploadServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding(UTF-8); // 获取上传的文件部分 Part part request.getPart(image); String fileName part.getSubmittedFileName(); // 生成唯一文件名防止覆盖 String newName System.currentTimeMillis() _ fileName; // 获取上传目录的绝对路径 String uploadPath getServletContext().getRealPath(/upload); File uploadDir new File(uploadPath); if (!uploadDir.exists()) { uploadDir.mkdirs(); } // 写入文件 part.write(uploadPath File.separator newName); // 把文件名返回给页面用于存入数据库 request.setAttribute(imageName, newName); request.getRequestDispatcher(/admin/addProduct.jsp).forward(request, response); } }逻辑说明MultipartConfig注解声明这个 Servlet 处理文件上传maxFileSize限制单文件大小。getSubmittedFileName()获取原始文件名System.currentTimeMillis()加前缀避免同名覆盖。getRealPath(/upload)拿到的是项目部署后的实际路径注意这个路径在 Tomcat 重启后可能会变所以生产环境一般存到项目外的固定目录。参数说明part.write()的路径要用File.separator而不是硬编码/否则在 Windows 上可能出问题。如果上传后图片显示不出来检查 Tomcat 的server.xml有没有配置upload目录的映射或者直接在 JSP 里用${pageContext.request.contextPath}/upload/${imageName}引用。5. 部署排错与答辩准备那些让你当场翻车的细节5.1 启动报错的排查顺序Tomcat 启动失败是最常见的问题排查要按顺序来。先看控制台第一段报错通常是ClassNotFoundException或NoClassDefFoundError说明 jar 包缺失。常见缺失的包有mysql-connector-java、jstl、standard、commons-fileupload、commons-io。把这些 jar 放到WEB-INF/lib下面然后在 Eclipse 里刷新项目。如果报Port 8080 already in use说明 8080 端口被占了。解决办法是改 Tomcat 的conf/server.xml把 Connector 的 port 改成 8081 或别的。或者用命令查一下谁占了端口# Windows 下查端口占用 netstat -ano | findstr :8080 # 根据 PID 结束进程 taskkill /PID 进程号 /F如果报The server time zone value ?D1ú±ê×?ê±?? is unrecognized这是 MySQL 8.0 的时区问题在连接 URL 里加serverTimezoneAsia/Shanghai就能解决。5.2 页面 404 和 500 的区分404 是找不到资源500 是服务器内部错误。404 常见原因JSP 文件路径写错、Servlet 的WebServlet注解值和访问地址不一致、web.xml里配置的url-pattern和实际请求不匹配。排查方法是在浏览器地址栏确认访问路径然后对照项目里的文件位置。500 错误要看 Tomcat 控制台的异常堆栈通常翻到最下面几行找到Caused by后面的内容。如果是NullPointerException大概率是某个对象没初始化就调用了方法比如 Session 里取出来的 user 是 null 但没判断。如果是SQLException检查 SQL 语句和数据库表结构是否对应。5.3 答辩时老师会问什么根据我帮学生模拟答辩的经验老师的问题集中在几个方向数据库表是怎么设计的有没有考虑范式Session 和 Cookie 的区别是什么你的项目里怎么用的如果两个人同时买最后一件商品库存怎么保证不超卖SQL 注入怎么防的项目里最难的部分是什么怎么解决的。这些问题不需要背标准答案结合你的代码回答就行。比如库存超卖的问题你就说在扣库存的 SQL 里加了stock ?条件并且用了事务如果更新影响行数为 0 就回滚。这样回答既具体又体现你理解了代码。5.4 常见踩坑记录坑一导入项目后所有 JSP 页面报「The superclass javax.servlet.http.HttpServlet was not found on the Java Build Path」现象Eclipse 里 JSP 文件满屏红叉但代码本身没问题。原因项目没有关联 Tomcat 运行时库。解决右键项目 → Properties → Java Build Path → Libraries → Add Library → Server Runtime → 选 Tomcat 8.5。坑二中文乱码页面显示问号或方块现象数据库里存的中文查出来是乱码或者表单提交后变成????。原因三个地方的编码没统一——JSP 页面pageEncoding、Servlet 的request.setCharacterEncoding、数据库连接 URL 的characterEncoding。解决JSP 头部写% page contentTypetext/html;charsetUTF-8 pageEncodingUTF-8 %Servlet 里第一行写request.setCharacterEncoding(UTF-8)数据库 URL 加characterEncodingutf8数据库和表的字符集设为utf8mb4。坑三修改 Java 代码后刷新页面没变化现象改了 Servlet 里的逻辑重启 Tomcat 后还是旧的行为。原因Eclipse 默认没有开启自动编译或者 class 文件没有重新部署到 Tomcat 的webapps目录。解决Project → Build Automatically 勾上然后 Project → Clean 一下再重启 Tomcat。如果还不行把 Tomcat 的work目录清空。坑四图片上传后访问 404现象上传成功数据库里也有文件名但页面上图片显示不出来。原因图片存到了getRealPath(/upload)返回的目录但那个目录在 Tomcat 的webapps下Eclipse 部署时可能没有同步过去。解决在项目WebContent下新建upload目录放一个.gitkeep文件占位确保部署时目录存在。或者配置 Tomcat 的虚拟路径映射。坑五订单生成后购物车没清空现象下单成功但购物车里的商品还在。原因清空购物车的 SQL 没有放在事务里或者user_id条件传错了。解决确认DELETE FROM cart WHERE user_id ?在conn.commit()之前执行并且参数是当前登录用户的 ID。6. 从能跑到能讲把毕业设计变成自己的东西把项目跑起来只是第一步答辩时老师更看重你是不是真的理解这套代码。我的习惯是每改一个功能就画一张流程图标清楚数据从哪个页面提交、经过哪个 Servlet、调用哪个 DAO、最终落到哪张表。比如「用户下单」这条链路cart.jsp点击结算 →OrderServlet接收请求 →OrderService.createOrder()处理事务 →OrderDao插入订单 →ProductDao扣库存 → 重定向到orderSuccess.jsp。画完这张图老师问什么你都能顺着讲。进阶一点的做法是给项目加两个小功能不用太复杂但能体现你的思考。比如加一个「商品搜索」功能在ProductDao里加一个searchByName方法用LIKE模糊查询// 按商品名模糊搜索 public ListProduct searchByName(String keyword) throws Exception { ListProduct list new ArrayList(); String sql SELECT * FROM product WHERE name LIKE ? ORDER BY id DESC; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { // 前后加百分号实现模糊匹配 ps.setString(1, % keyword %); 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); } } return list; }这个方法的参数是搜索关键词%是 SQL 通配符表示任意字符。注意LIKE查询在数据量大时性能不好但毕业设计的数据量完全够用。加完这个功能答辩时你就可以说「我在原有基础上扩展了搜索模块用 PreparedStatement 防止注入通过 LIKE 实现模糊匹配」这比干巴巴地说「我跑通了」有说服力得多。另一个值得加的验证手段是写一个简单的测试类不用 JUnit直接main方法跑// TestConnection.java 快速验证数据库连通性 public class TestConnection { public static void main(String[] args) { try { Connection conn DBUtil.getConnection(); System.out.println(数据库连接成功 conn.getCatalog()); ProductDao dao new ProductDao(); ListProduct list dao.findAll(0); System.out.println(商品数量 list.size()); conn.close(); } catch (Exception e) { System.out.println(连接失败 e.getMessage()); e.printStackTrace(); } } }这个类的作用是在不启动 Tomcat 的情况下验证数据库配置对不对。如果main方法能跑通说明 JDBC 配置没问题Tomcat 里报错就是别的原因。如果main方法都跑不通那就专心排查数据库连接不用在 Tomcat 上浪费时间。最后说一个我自己的习惯拿到任何一份别人的代码先不要改先跑通跑通之后把每个文件的职责用一句话写在注释里。比如LoginServlet上面写「处理登录请求验证成功后写 Session」ProductDao上面写「商品表的增删改查」。写完之后你对整个项目的理解会清晰很多答辩时也不容易卡壳。这套 JSP 购物系统虽然技术栈不新但它是理解 Java Web 完整链路的最好载体把这条链路吃透后面学 Spring Boot 会轻松很多。希望帮到你。本文还有配套的精品资源点击获取
返回列表