news 2026/11/6 12:07:32

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
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.0,Tomcat 版本对应 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.jar,MySQL 8.0 用mysql-connector-java-8.0.x.jar,两者的连接 URL 写法不同。5.7 的 URL 是jdbc:mysql://localhost:3306/数据库名,8.0 必须加时区和 SSL 参数:jdbc:mysql://localhost:3306/数据库名?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8。很多同学导入项目后报「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" + "?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8"; // 改成你自己的 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, address
product商品信息id, name, price, stock, image, category_id
category商品分类id, name, parent_id
cart购物车id, user_id, product_id, quantity
orders订单主表id, user_id, total_price, status, create_time
order_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 List<Product> findAll(int categoryId) throws Exception { List<Product> list = new ArrayList<>(); String sql = "SELECT * FROM product WHERE 1=1"; 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 '1'='1,用拼接字符串的方式就会把整张表查出来。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;charset=UTF-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, List<Cart> 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 里加serverTimezone=Asia/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 contentType="text/html;charset=UTF-8" pageEncoding="UTF-8" %>,Servlet 里第一行写request.setCharacterEncoding("UTF-8"),数据库 URL 加characterEncoding=utf8,数据库和表的字符集设为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 List<Product> searchByName(String keyword) throws Exception { List<Product> 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(); List<Product> 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 会轻松很多。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/27 5:24:33

SSM+Vue3设备维修管理系统毕设实战:从选型到联调避坑

简介&#xff1a;本资源为基于SSM与Vue3实现的设备维修管理系统毕业设计项目&#xff0c;面向计算机相关专业需要完成毕设的学生及Java全栈初学者。系统采用B/S架构与前后端分离模式&#xff0c;后端以Java语言开发&#xff0c;前端使用Vue3配合ElementPlus构建界面&#xff0c…

作者头像 李华
网站建设 2026/10/26 6:40:38

台湾道路矢量数据分级处理:QGIS拆分shp并导出WKT全攻略

简介&#xff1a;台湾省道路数据最新分级精确到乡道矢量数据包&#xff0c;面向地理信息系统开发、交通规划与地图制图等专业人员&#xff0c;适合需要道路路网底图进行空间分析、可视化或研究台湾地区道路分级体系的场景。数据分为三个维度&#xff1a;城市一级至四级道路线要…

作者头像 李华
网站建设 2026/10/28 8:26:18

Python打卡第32天

浙大疏锦行 类的装饰器&#xff1a; 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 def class_logger(cls): """ A class decorator that adds a lo…

作者头像 李华
网站建设 2026/10/28 3:29:24

RFID抗金属标签在智能制造中的选型与部署实战指南

1. 从一张“贴不上”的标签说起&#xff1a;RFID抗金属标签到底解决了什么问题做过智能制造产线改造的人大概率都遇到过这种尴尬&#xff1a;花大价钱上了RFID读写器和系统&#xff0c;结果在金属工装板、金属托盘、金属货架上贴标签时&#xff0c;读取率直接崩盘。普通不干胶R…

作者头像 李华
网站建设 2026/10/28 6:12:37

番茄识别数据集构建与训练避坑实战指南

简介&#xff1a;面向目标检测任务的番茄识别数据集&#xff0c;共含4786张番茄图片的标注信息&#xff0c;类别覆盖未成熟番茄、成熟番茄和患病番茄&#xff0c;适用于YOLO系列、Faster Rcnn、SSD等主流目标检测模型的训练与评估。压缩包共2000个文件&#xff0c;以txt标签文件…

作者头像 李华
网站建设 2026/11/5 8:52:28

Python开心消消乐源码实战:游戏循环、匹配判定与改造技巧

简介&#xff1a;Python项目实例源码&#xff0c;实现经典三消游戏开心消消乐&#xff0c;面向Python初学者与游戏开发爱好者&#xff0c;完整呈现休闲消除类游戏从界面搭建到核心逻辑的实现过程。资源共138个文件&#xff0c;涵盖92张png游戏素材、20组mp3/ogg背景音乐与音效、…

作者头像 李华