☰
jsp+servlet拍卖管理系统部署指南:从建库到避坑全流程
2026/10/29 15:41:30 网站建设 项目流程

简介:这是一套基于jsp+servlet的拍卖管理系统毕业设计源码包,面向Java Web初学者和毕业设计开发者,完整覆盖商城类项目中商品竞拍、分类管理、订单管理等典型模块。系统前端采用jsp和jquery,后端基于servlet与jdbc,并区分管理员与用户两类角色,便于理解传统Java Web分层开发思路。压缩包共含438个文件,大小约23.19MB,主要包含79个jsp页面、42个css与42个js前端资源、8个java后端类、1个sql数据库脚本,以及jar包和图片资源,涵盖项目运行和二次开发所需的各类文件,可直接导入IDEA配合Mysql、Tomcat使用。目前已有26人浏览学习。对于需要完成课程设计或毕业设计的读者,资源内含完整的前后端实现、数据库脚本和可运行的项目结构,既可按用户端演示竞拍流程,也可在管理员端体验分类、商品、订单和留言等管理功能,适合作为系统二次开发与功能扩展的基础。

1. 这套 jsp+servlet 拍卖管理系统,值得花半天跑起来

每年到这个节点,总有人找 jsp+servlet 的完整项目做毕业设计。这套拍卖管理系统就是典型的 servlet 时代作业:前端 jsp+jquery,后端 servlet+jdbc,角色分管理员和普通用户。它不是只有登录注册的壳子,商品竞拍、分类管理、订单管理、在线留言、轮播图、友情链接都做齐了,数据库脚本是完整初始化脚本,导入就能出数据,不用自己建表。适合两类人:拿它当毕业设计底子,或者想快速把 servlet+jdbc 链路从头串一遍。下面按我实际拆项目的顺序,从建库、部署到避坑逐层展开,踩坑部分都是血泪经验,照着走能省一下午。

2. 先让数据库站起来:导入 SQL 脚本与连库参数

2.1 确认 SQL 脚本内容与字符集

拿到资源别急着双击导入。先用文本编辑器打开 SQL 脚本,重点看四件事:有没有 CREATE DATABASE 建库语句;表结构里是否包含用户表、商品表、竞拍记录表、订单表和留言表;表引擎是不是 InnoDB;字符集是不是 utf8 或 utf8mb4。这些都是影响后面能不能跑通的前提。

我常见到的情况是脚本里不带建库语句,需要手动先建一个空库。库名可以叫 auction_db,简单直接,后面连库参数不容易写错。建库语句如下:

-- 创建拍卖系统数据库,支持中文和特殊字符 CREATE DATABASE IF NOT EXISTS auction_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;

utf8mb4 向下兼容 utf8,能存 emoji 和特殊符号,比纯 utf8 稳。排序规则选 general_ci 是历史习惯,绝大部分课程设计都用的它,碰到中文排序差异时可以换成 utf8mb4_unicode_ci,但那属于后话。

库建好后导入数据。命令行导入是最稳的方式,不受图形工具编码预判的影响。如果你的 MySQL bin 目录没进 PATH,可以这样写:

# 注意把路径换成你解压后 SQL 脚本的实际位置 mysql -uroot -p --default-character-set=utf8mb4 auction_db < /e/project/auction/auction_db.sql

这里 utf8mb4 参数确保脚本里的中文字面量按 UTF-8 解析。导入成功时命令没有任何输出,直接回到系统提示符。看到 ERROR 1064 这类语法错误,大概率是脚本注释格式和当前 MySQL 版本不兼容,检查脚本尾部有没有乱码注释。

导入完成后,立刻查一遍核心表数量和初始数据,别等到启动项目再回头看是不是导入失败:

USE auction_db; SHOW TABLES; -- 下面三条分别是用户、商品、竞拍记录的初始条数 SELECT COUNT(*) AS user_cnt FROM tb_user; SELECT COUNT(*) AS product_cnt FROM tb_product; SELECT COUNT(*) AS bid_cnt FROM tb_bid;

SHOW TABLES 能看到所有表名,三个 COUNT 分别数用户、商品、竞拍记录。商品表初始有数据,页面商品列表就是满的;全是 0 也没关系,只是少了演示数据,用管理员账号在后台上架几条就有。这一步花一分钟核对,能避免后面浪费时间在空库里找 bug。

2.2 核心表结构速览

导入完成后,建议用下面这条 SQL 快速看一下竞拍记录和订单的字段分布,不需要逐行读脚本:

-- 查看竞拍记录表结构 SHOW COLUMNS FROM tb_bid; -- 查看订单表结构 SHOW COLUMNS FROM tb_order;

竞拍记录表里通常有 product_id、user_id、price、bid_time 四个核心字段,分别存竞拍商品、竞拍人、出价和出价时间。订单表里一般有订单号、商品 id、买家 id、状态、下单时间。这两张表的关系,决定你答辩时能不能讲清楚「竞拍成功怎么变成订单」。

从数据模型来看,这个系统的核心是一个「用户-商品-竞拍」三角关系:用户表放账号密码和联系方式,商品表放起拍价、当前价、分类和描述,竞拍记录表对每一次出价留痕。订单表可以理解为竞拍结果的落地,管理员在后台确认竞拍有效之后,把它转成订单。理解这个关系之后看代码会顺很多,因为 servlet、service、dao 三层其实就是围绕这几张表在转。

顺带一提,后台里的分类管理、商品管理、留言管理,本质都是针对单张表的增删改查。先把表结构弄明白,后面看 servlet 里那些重复的 list、add、update、delete 分支,就完全不慌。

2.3 连库参数修改与 MySQL 版本差异

数据库结构就绪后,把连接信息改到项目里。这套资源没有 Maven,连接配置一般集中在 src 下的 db.properties 或 JdbcUtils.java。打开文件后,核心是三行参数:

# MySQL 5.7 驱动类名 jdbc.driver=com.mysql.jdbc.Driver # 数据库地址,auction_db 对应前面建好的库名 jdbc.url=jdbc:mysql://localhost:3306/auction_db?useUnicode=true&characterEncoding=utf8&useSSL=false jdbc.username=root jdbc.password=你的数据库密码

driver 这行是坑最多的。MySQL 5.7 用 com.mysql.jdbc.Driver,如果本机是 MySQL 8.0,必须改成 com.mysql.cj.jdbc.Driver,并且 URL 里追加 serverTimezone=Asia/Shanghai,否则启动报 ClassNotFoundException 或 CST 时区异常。资源标的环境是 MySQL 5.7.26,最好照着装,而不是用最新的 8.0,因为老代码很多地方是按 5.x 写的,少踩坑。

用户名和密码按本机 MySQL 实际值改。root 密码为空的话 password 可以直接不写,但我不建议,空密码本地调试有时会被连接器拒绝,顺手设一个简单密码更省事。

提示:URL 里的 useSSL=false 建议保留,本地开发不用 SSL,能少一些告警,对纯课设系统来说是正确选择。

3. 在 IDEA 2021.3 里部署:普通 Web 工程还是 Maven

3.1 判别工程类型与依赖检查

用 IDEA 打开项目根目录后,第一步不是直接点运行,而是先判别工程类型。看根目录有没有 pom.xml,有就是 Maven 工程;没有就是传统普通 Web 工程。jsp+servlet 课设资源里大部分是后者,目录结构大致是 src + WebContent,WebContent 下面有 WEB-INF,lib 目录里躺着 jar 包。

这种结构导入后,IDEA 不会自动帮你建立 Module 和 Web Facet,需要手动配置。先检查依赖 jar 是否齐全,重点看 WEB-INF/lib 下有没有这几样:mysql-connector-java 驱动、jstl-1.2.jar、servlet-api.jar。servlet-api 其实不需要放进 lib,因为 Tomcat 自带,但如果项目里放了也不会冲突,最怕的是缺失和重复。检查完依赖,再处理工程结构。

3.2 Project Structure 四项配置

打开 Project Structure(快捷键 Ctrl+Alt+Shift+S),按顺序确认四件事。

第一,Project SDK 必须选 1.8,Language Level 也选 8。高出这个版本,比如用 JDK 17 编译老代码,会遇到反射访问报错和一箩筐警告,没必要自找麻烦。第二,Modules 里把 src 目录标记为 Sources,把 WebContent 目录标记为 Web Resources,同时添加 Web Facet。第三,Facets 里确认 Web 资源目录指向 WebContent 而不是 src,很多导入后页面 404 的问题就是这里指错了。第四,Artifacts 里要存在 war exploded 这个构建产物,没有就点加号从 Web Application: Exploded 创建。

四项配置看着繁琐,但每一步都是部署阶段的前置条件。跳过任何一项,启动时要么控制台不报错但页面打不开,要么直接提示缺少依赖。我在拆项目时习惯把这四项当成固定清单轮流过,比出了错再翻配置快得多。

3.3 新建 Tomcat 7 运行配置

运行配置这块相对固定。点右上方 Add Configuration,选择 Tomcat Server -> Local,然后按下面的表填:

配置项建议值说明
Application serverTomcat 7.0.73 安装目录不是 8.5,不是 10
JRE1.8与 Project SDK 保持一致
HTTP port8080被占用时往下看
Deployment项目名:war exploded通过 + 号选择 Artifacts
Application context/auction与项目内跳转路径一致

Application context 是 404 高发区。项目源码里如果硬编码了 /auction/index.jsp 这种路径,本地部署上下文就必须是 /auction,写成 / 或 /web 都会找不到页面。不确定到底写什么,就到项目里搜一下 sendRedirect 和 getRequestDispatcher 的路径,看到前缀是什么就填什么。

选 Tomcat 7 而不是 Tomcat 10,原因是老项目的 JSP 和 Servlet 代码用的是 javax.servlet 包名,Tomcat 10 换成了 jakarta.servlet,直接编译不过。这不是你代码写得不好,是 servlet 规范版本跨代,本地复现时绕开即可。

启动之前顺手验证两件事:Tomcat 7 的 bin 目录版本和 JDK 8 匹配;8080 端口没被其他服务占用。检查端口可以用:

# Windows 下查看 8080 端口占用的进程 netstat -ano | findstr 8080

端口被占用时有两条路:杀掉占用进程,或者改 Tomcat 的 conf/server.xml,把三个 Connector 端口统一改掉。我一般直接改端口,因为杀进程容易误伤其他服务。端口改完之后,Tomcat 运行配置里的 HTTP port 也要同步改,两个地方不一致,启动照样失败。

4. 核心链路拆解:商品竞拍是怎么从页面走到数据库

4.1 页面请求到 Servlet 路由

商品详情页的竞拍表单,核心要素是 form 的 action、input 的 name 和提交按钮。前端用 jquery 做部分交互,但真正的数据提交是同步或异步发到 servlet。在 jsp 页面里找到竞拍表单,通常长这样:

<form action="productServlet?action=addBid" method="post"> <input type="hidden" name="productId" value="${product.id}" /> <input type="text" name="bidPrice" /> <button type="submit">立即竞拍</button> </form>

表单里两个参数:productId 是当前商品的隐藏字段,bidPrice 是用户输入的价格。action=addBid 是后端路由标识。看懂了这行,再去 servlet 里找对应的 addBid 分支,整条链路就从这里开始。答辩时讲「请求是怎么进后端的」,其实就是一句话:浏览器根据 form 的 action 发起 post 请求,Tomcat 根据 web.xml 里的 servlet-mapping 找到 ProductServlet,servlet 根据 action 参数决定执行哪个方法。

如果项目里用的是注解 @WebServlet("/productServlet"),映射效果一样,只是少了 web.xml 里的配置段。两种方式都正常,看资源自带的是哪种,不需要强行改。

4.2 Servlet 取参、校验与跳转

到了 ProductServlet 的 doPost,常见写法是先统一拿到 action 参数再分发给不同方法。竞拍这个动作里,除了参数,还要从 session 里取当前登录用户,因为竞拍必须记录是谁出的价。session 为空说明用户没登录,直接踢回登录页,这是 servlet 项目里最基础但也最必要的登录校验。

protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 必须放在 getParameter 之前,否则中文参数乱码 req.setCharacterEncoding("UTF-8"); String action = req.getParameter("action"); if ("addBid".equals(action)) { int productId = Integer.parseInt(req.getParameter("productId")); double bidPrice = Double.parseDouble(req.getParameter("bidPrice")); User loginUser = (User) req.getSession().getAttribute("loginUser"); if (loginUser == null) { resp.sendRedirect("login.jsp"); return; } boolean ok = bidService.addBid(productId, loginUser.getId(), bidPrice); if (ok) { resp.sendRedirect("productServlet?action=detail&id=" + productId); } else { req.setAttribute("error", "出价失败,请检查价格是否高于当前价"); req.getRequestDispatcher("product_detail.jsp").forward(req, resp); } } }

这段代码有三个细节值得注意。第一,req.setCharacterEncoding("UTF-8") 必须在 getParameter 之前调用,顺序错了中文参数就会乱码。第二,Integer.parseInt 和 Double.parseDouble 没有做异常兜底,用户填入非数字时会抛 NumberFormatException,二开时最好改成 try-catch 并回填提示。第三,登录验证用的是 session 里的 loginUser,而不是再次查询数据库,这也是常见做法,session 在登录时已经存好,没必要重复查表。

参数都取到之后,调用的是 service 层的 addBid 方法,servlet 只负责传参和跳转。这种分层的好处是,同一个方法可以同时被 doPost 和其他入口复用,事务逻辑不会散落在请求处理里。

4.3 竞拍落库:事务与价格校验

servlet 拿到参数后调 service,service 再调 dao。竞拍这个操作涉及两张表,先向竞拍记录表插入一条出价,再把商品表的当前价更新为最新出价。这两步必须保证原子性,否则会出现记录了但价格没变的脏数据。用 JDBC 手动管理事务的标准写法如下:

Connection conn = null; try { conn = JdbcUtils.getConnection(); // 关闭自动提交,插入和更新要作为一个整体 conn.setAutoCommit(false); PreparedStatement ps1 = conn.prepareStatement( "INSERT INTO tb_bid(product_id, user_id, price, bid_time) VALUES (?,?,?,NOW())"); ps1.setInt(1, productId); ps1.setInt(2, userId); ps1.setDouble(3, bidPrice); ps1.executeUpdate(); PreparedStatement ps2 = conn.prepareStatement( "UPDATE tb_product SET current_price = ? WHERE id = ? AND current_price < ?"); ps2.setDouble(1, bidPrice); ps2.setInt(2, productId); ps2.setDouble(3, bidPrice); int updated = ps2.executeUpdate(); if (updated == 0) { conn.rollback(); return false; } conn.commit(); return true; } catch (Exception e) { if (conn != null) conn.rollback(); throw new RuntimeException("竞拍失败", e); } finally { JdbcUtils.close(conn); }

这段代码比单纯插一条数据多了两个关键点。第一个是 UPDATE 语句里的 current_price < ? 条件,让数据库在价格不高时直接返回 0 行受影响,代码借此判断出价无效并回滚,把价格校验从代码层下沉到了 SQL 层,更稳。第二个是 finally 里统一关闭连接,避免连接泄漏。JDBC 项目里最常见的问题就是连接不关,积累到一定量后 Tomcat 连接池被耗尽,系统再也连不上数据库。

service 层存在的意义就在这里。这段事务逻辑如果直接写在 servlet 里也能跑,但负责处理 HTTP 的 servlet 里揉进数据库事务,代码会变得难测,业务动作也没法复用。常见做法是在 service 层声明一个 addBid 方法,一个方法对应一个事务边界,servlet 只负责取参和跳转。答辩时能说出「controller 只做请求分发,事务在 service 层」,比单纯念源码要高一个档次。

5. 本地复现避坑:五个常见问题与排查顺序

5.1 启动报 ClassNotFoundException: com.mysql.jdbc.Driver

现象:启动 Tomcat 或访问首页时,控制台报 java.lang.ClassNotFoundException,指向 com.mysql.jdbc.Driver,页面 500。

原因:项目 lib 里没有 mysql-connector 的 jar,或者 jar 版本太旧,再或者 jar 在磁盘上存在但部署产物里没有打包进去。最后这种情况最隐蔽,文件明明在 WEB-INF/lib 里,IDEA 却没把它带进 Artifacts。

解决:按顺序做三步。第一步,打开 WEB-INF/lib 目录,确认 mysql-connector-java 存在,没有就下载一个对应 MySQL 5.7 的驱动版本放进去。第二步,打开 Project Structure -> Artifacts -> Output Layout,展开 WEB-INF/lib,确认 jar 在列表里,不在就右键加进来。第三步,重新构建项目重启 Tomcat。多数情况是第二步遗漏,IDEA 不会自动追踪手动丢进目录的 jar。

5.2 页面展示的中文全部变成问号

现象:列表页、详情页、后台管理页所有中文都显示为 ??,或者存进去是正常的,读出来却是乱码。

原因:字符集问题有三个层次。表或库的字符集不是 utf8;JDBC URL 里没有 characterEncoding;Servlet 没有在入口设置请求编码。三个层次任何一个出问题都会乱码,而且经常同时存在。

解决:先用 2.1 节的建库语句重建库,确保建库时指定 utf8mb4。再检查 db.properties 的 URL,确认包含 useUnicode=true&characterEncoding=utf8。最后在 web.xml 里加一个全局编码过滤器,避免逐个 servlet 去加 setCharacterEncoding。项目里已经有 CharacterEncodingFilter 的,检查它的初始化参数 encoding 是否等于 utf8。

5.3 登录成功后跳回登录页或 404

现象:提交正确的账号密码,点击登录后没有进入主页而是回到登录页,或者地址栏出现不存在的路径,页面 404。

原因:回到登录页通常是 session 没写对,登录逻辑没有把用户放进 session,主页的登录校验一查 session 为空就把你踢回去。404 通常是跳转路径写死成了绝对路径,或者部署上下文和跳转路径前缀不一致。

解决:打开登录 servlet,确认登录成功后执行了 request.getSession().setAttribute("loginUser", user),没有这行,页面一刷新 session 里就是空的。跳转路径统一用相对路径或动态拼接 request.getContextPath(),不要写死绝对路径。这样 Application context 改成什么都能跟得上。

5.4 商品分类筛选不生效

现象:主页分类下拉框选择某个分类,点查询后商品列表没有变化,仍然显示所有商品。

原因:多半是 form 里 select 的 name 和 servlet 里 getParameter 取的名字不一致,前端叫 categoryId,后端取 category_id,这种细碎的不匹配在课设代码里特别常见。另一个可能是分类表的 id 和商品表的 categoryId 对不上,关联查询查不出数据,但这种情况通常表现为结果为空,而不是全部显示。

解决:打开商品列表页的 jsp,找到表单里 select 的 name 属性,再到商品搜索 servlet 里看 getParameter 的参数名,两个名字必须完全一致。确认一致后,直接在数据库里跑一遍分类关联 SQL,看看手动执行和代码执行的结果是否一样。这个坑排查起来不难,但很容易被忽略,因为前端和后端文件隔着好几层目录。

5.5 富文本上传的图片刷新后消失

现象:管理员在后台用富文本编辑商品描述,上传了图片,当时能看到。重启 Tomcat 或重新部署后,图片变成破图,前台商品详情页也显示不出。

原因:项目把图片保存到了工程内部的某个 upload 目录,IDEA 重新部署 war exploded 时,这个目录被当作旧部署产物清掉了。另一个常见原因是图片保存时记的是磁盘绝对路径,浏览器拿这个路径去请求到的地址根本不存在,自然 404。

解决:把图片存储目录改到 Tomcat 的 webapps 之外的固定位置,或项目里配置文件指向的绝对路径。访问图片时,用 /upload/xxx.jpg 这种相对路径拼 URL,不要用物理路径。排查顺序永远是先确认图片文件在磁盘上存在,再看浏览器网络面板里图片请求的 HTTP 状态码,别一上来就怀疑数据库字段。

6. 验收与二开:把课设变成能答辩的完整项目

系统跑通只是第一步,答辩和交作业要有验证步骤。我拆这类资源会强制走一遍验收清单:管理员账号登录,新增分类、发布商品、处理订单、配置轮播图和友情链接;新注册一个用户,修改资料、改密码、发布留言;挑一个商品走完竞拍流程,确认个人中心能看到竞拍记录,商品当前价同步更新;最后管理员在竞拍管理里看到这笔竞拍,并转成订单。走完这轮,系统功能就算闭环了。

答辩最有价值的是三个功能闭环:竞拍与订单怎么衔接,留言与后台回复怎么对接,轮播图怎么由后台配置决定前台展示。任意一个闭环讲清楚数据流转,都能证明你真的读过代码。演示顺序按用户视角来:先注册登录,再浏览拍卖,再出价,再到个人中心看记录,最后管理员端确认和出单,这条线最自然。

如果打算继续改,三个方向性价比最高。第一,把数据库连接参数抽到 db.properties,类里只留读取逻辑,这是代码规范最直观的加分点。第二,竞拍价格同时加前端 JS 校验和后端 SQL 校验,前端校验提示友好,后端校验保证数据安全。第三,给商品列表加分页,每页十条,目前很多课设是一页展示全部,商品一多演示时极其尴尬。

我从拆项目里学到的习惯是:任何 jsp+servlet 资源,先花半小时把数据库版本、JDK 版本、Tomcat 版本三个信息确认一遍,再动手部署。版本环境对不齐,后面所有排查都会在同一批错误里反复横跳。这也是我现在拆新项目第一件事就是列环境清单的原因。希望这份拆解记录能帮你在拍卖系统上少走弯路,顺利跑通、顺利答辩。

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询