ARTICLE DETAIL

资讯详情

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

JSP+MySQL校园二手交易系统设计与部署实战:从表结构到Servlet避坑

JSP+MySQL校园二手交易系统设计与部署实战:从表结构到Servlet避坑 简介基于JSP与MySQL实现的校园二手物品交易系统采用B/S架构与SSM框架进行开发面向毕业设计、课程设计以及正在学习JavaWeb整合开发的读者解决校园内二手物品信息流转与交易管理问题。系统划分为前台和后台前台支持用户注册登录、发布物品、分类搜索、下单购买以及个人中心维护后台提供用户、物品、订单和分类等维度的管理功能。压缩包为7z格式共202个文件约12.63MB包含Java源码及class编译文件、JSP页面、CSS与JS等前端资源、XML与Properties配置、SQL数据库脚本、Word说明文档以及PNG/JPG等图片素材。当前已有21人浏览学习。从内容预览可以看出代码按照Controller、Service、Mapper三层组织结构清晰适合导入开发工具后结合SQL脚本快速搭建环境进而理解SSM整合流程并可直接作为毕业设计、课程设计或二次开发的基础。1. 基于JSP和MySQL的校园二手物品交易系统到底能拿来做什么先说结论用一个JSPMySQL搭起来的校园二手物品交易系统最典型的使用场景是毕业设计和课程设计交差其次是让你在三个月后还能回头读懂自己的JavaWeb代码。它解决的是一所学校里学生之间买卖二手书、闲置数码、考研资料这类低频、实名、小范围交易的完整链路——不是做成闲鱼而是做成一个能演示“注册、登录、发布闲置、浏览列表、下单或留言、后台管理”全流程的教学范本。为什么这个题目在JSP课设里长盛不衰因为它覆盖面刚好JSP页面负责渲染Servlet负责请求分发MySQL负责三张以上业务表的增删改查恰好把JavaWeb课程最重要的几个知识点全串起来。写系统之前我习惯先把“提交一个表单后发生了什么”这个问题想清楚剩下就是照着表的字段去堆代码。这篇笔记从数据库表设计开始到部署、参数调整、避坑最后给你几个不加代码量的加分项全程按我实际做课设和帮人改稿子的顺序来。2. 先把数据库立住校园二手物品交易系统的表设计与SQL脚本2.1 五张核心表一张都不能少做课程设计最怕的是表设计理不清写代码时反复改结构。校园二手物品交易系统的业务其实只有四个动作用户登录、发布商品、浏览商品、买卖双方达成交易。围绕这四个动作我一般会建五张表tb_user用户表存学号、姓名、密码、联系方式、头像路径。tb_category分类表存书籍、数码、生活用品、其他这几个固定分类。tb_goods商品表关联用户和分类存标题、描述、原价、售价、图片路径、发布时间、状态。tb_order订单表关联买家、卖家、商品存成交价格、下单时间、订单状态。tb_admin管理员表后台登录用独立于前台用户。这里的核心是tb_goods的状态字段。很多新手会把“已售出”做成删除商品导致后台查不到历史交易记录这是最常见的翻车设计。正确做法是保留status字段用0表示上架中、1表示已售出、2表示已下架列表查询永远过滤status 0。SQL脚本如下字符集统一用utf8mb4排序规则用utf8mb4_general_ci-- 校园二手物品交易系统建库脚本 V1.0 CREATE DATABASE IF NOT EXISTS campus_trade DEFAULT CHARACTER SET utf8mb4; USE campus_trade; -- 用户表 CREATE TABLE tb_user ( id INT PRIMARY KEY AUTO_INCREMENT, student_no VARCHAR(20) UNIQUE NOT NULL COMMENT 学号登录账号, username VARCHAR(50) NOT NULL COMMENT 昵称, password VARCHAR(64) NOT NULL COMMENT 密码用MD5加密之后存入, phone VARCHAR(20) DEFAULT NULL, avatar VARCHAR(255) DEFAULT /upload/avatar/default.jpg, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB; -- 分类表 CREATE TABLE tb_category ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(30) NOT NULL UNIQUE COMMENT 分类名称, sort INT DEFAULT 0 COMMENT 排序权重 ) ENGINEInnoDB; -- 商品表 CREATE TABLE tb_goods ( id INT PRIMARY KEY AUTO_INCREMENT, user_id INT NOT NULL COMMENT 发布者ID, category_id INT NOT NULL COMMENT 分类ID, title VARCHAR(100) NOT NULL, description TEXT, original_price DECIMAL(10,2) DEFAULT 0, sell_price DECIMAL(10,2) NOT NULL, image VARCHAR(255) DEFAULT NULL COMMENT 商品图片存放路径, status TINYINT DEFAULT 0 COMMENT 0上架 1已售出 2下架, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, KEY idx_user (user_id), KEY idx_category (category_id), KEY idx_status (status), CONSTRAINT fk_goods_user FOREIGN KEY (user_id) REFERENCES tb_user(id), CONSTRAINT fk_goods_cate FOREIGN KEY (category_id) REFERENCES tb_category(id) ) ENGINEInnoDB; -- 订单表 CREATE TABLE tb_order ( id INT PRIMARY KEY AUTO_INCREMENT, order_no VARCHAR(32) UNIQUE NOT NULL COMMENT 订单编号用时间戳随机数生成, goods_id INT NOT NULL, buyer_id INT NOT NULL, seller_id INT NOT NULL COMMENT 冗余卖家ID避免连表查询, price DECIMAL(10,2) NOT NULL COMMENT 成交价, status TINYINT DEFAULT 0 COMMENT 0待确认 1已完成 2已取消, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, KEY idx_goods (goods_id), KEY idx_buyer (buyer_id), KEY idx_seller (seller_id) ) ENGINEInnoDB; -- 管理员表 CREATE TABLE tb_admin ( id INT PRIMARY KEY AUTO_INCREMENT, admin_name VARCHAR(50) UNIQUE NOT NULL, password VARCHAR(64) NOT NULL ) ENGINEInnoDB;2.2 这套表结构里藏着三个必调参数第一password字段我用VARCHAR(64)是为了存MD5或SHA256的十六进制结果。如果你的代码里用MD5工具类32位就够了如果用SHA-256就是64位。课程设计一般两种都行但一定不要明文存密码答辩时老师几乎必问。第二DECIMAL(10,2)不要写成FLOAT。二手物品价格虽然简单但答辩时老师会追一句“浮点数为什么会出现0.30000000000000004这种误差”有钱的字段一律用定点数这是MySQL的基本常识。第三create_time我全部使用DEFAULT CURRENT_TIMESTAMP让数据库自己写时间而不是在JSP里靠new Date()手动拼。这样数据插入时少一次代码层的时间格式化也避免服务器时区和数据库时区不一致导致差了8小时。2.3 数据初始化管理员账号和演示数据课程设计交上去老师第一件事肯定是打开系统看登录页面。所以预置一条管理员账号和几条商品演示数据非常必要-- 预设管理员初始密码 admin123MD5后 INSERT INTO tb_admin (admin_name, password) VALUES (admin, 21232f297a57a5a743894a0e4a801fc3); -- 两个学生用户 INSERT INTO tb_user (student_no, username, password, phone) VALUES (20210001, 张三, e10adc3949ba59abbe56e057f20f883e, 13800000001), (20210002, 李四, e10adc3949ba59abbe56e057f20f883e, 13800000002); -- 分类数据 INSERT INTO tb_category (name, sort) VALUES (教材书籍, 1), (数码产品, 2), (生活用品, 3), (运动健身, 4); -- 演示商品 INSERT INTO tb_goods (user_id, category_id, title, description, original_price, sell_price, status) VALUES (1, 1, 高数第六版 课后习题答案, 九成新有少量笔记适合期末突击, 45.00, 15.00, 0), (2, 2, 小米手环5 NFC版, 戴了半年充电线还在, 199.00, 80.00, 0);存储引擎我统一用InnoDB外键约束保留ON DELETE RESTRICT的默认行为即用户有未删除的商品时不能直接删用户。这对课程设计来说是合理的——老师不会考你怎么级联删除但外键能防止你写出删了用户导致商品悬空的逻辑。3. JSPServlet代码结构从一次登录请求看懂请求模型和MVC落法3.1 项目目录结构先按MVC把包建对拿到源码不是先看哪个文件而是先看目录。一个标准的JSPServlet工程不需要引入Spring全家桶但必须分清楚角色。我习惯用如下目录src/ com.campus.dao // 数据库操作最简单的手写JDBC com.campus.entity // 实体类对应五张表 com.campus.service // 业务逻辑放一些简单的判断 com.campus.servlet // 控制器接收请求、调service、转发JSP com.campus.util // DBUtil、MD5工具 WebContent/ index.jsp // 门户首页商品列表 user/login.jsp // 登录页 user/register.jsp goods/publish.jsp // 发布商品页 goods/detail.jsp // 商品详情 admin/admin.jsp // 后台管理 WEB-INF/web.xml upload/ // 商品图片上传目录如果你拿到的源码包没有service这一层那多半是Servlet直接写JDBC能跑但答辩时会被问“代码重复怎么办”。建议自己补一层。即使不补也要在答辩里说清楚你这是瘦控制器风格业务逻辑会抽取到独立类中。3.2 从登录功能拆解Servlet请求处理流程登录是每个用户的入口也是老师第一个试的功能。登录请求的完整链路值得逐行写清楚浏览器提交表单Servlet接收参数调Service查询tb_user有记录则把用户对象放进session重定向到首页否则返回错误提示。WebServlet(/login) public class LoginServlet extends HttpServlet { private UserService userService new UserService(); protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 1.设置请求编码这一步不写中文必乱码 request.setCharacterEncoding(UTF-8); response.setContentType(text/html;charsetUTF-8); String studentNo request.getParameter(studentNo); String password request.getParameter(password); String md5Pwd MD5Util.md5(password); User user userService.login(studentNo, md5Pwd); if (user null) { // 2.登录失败回登录页用request域携带错误信息 request.setAttribute(errorMsg, 学号或密码错误); request.getRequestDispatcher(/user/login.jsp).forward(request, response); } else { // 3.登录成功session保存用户信息 HttpSession session request.getSession(); session.setAttribute(loginUser, user); response.sendRedirect(request.getContextPath() /index.jsp); } } }这个短方法里有两个避坑点。第一request.getParameter拿到的是表单里的name值不设置编码时POST请求的中文大概率会乱。第二登录失败用forward成功用sendRedirect。前者保留请求域数据刷新页面会重复提交后者让浏览器URL变成首页避免按F5把表单再提交一次。这两段视频里的老师可能不会展开讲但面试或答辩时很加分。3.3 发布商品图片上传与状态字段的配合发布闲置是二手交易系统的核心操作这里的难点是文件上传。JSP端表单必须声明enctypemultipart/form-dataServlet端如果你用原生的getParameter去取标题拿到的永远是null。这是最常见的翻车点。WebServlet(/publish) public class PublishServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding(UTF-8); // 先取当前登录用户没登录则回登录页 HttpSession session request.getSession(); User loginUser (User) session.getAttribute(loginUser); if (loginUser null) { response.sendRedirect(request.getContextPath() /user/login.jsp); return; } // 用commons-fileupload解析multipart请求 DiskFileItemFactory factory new DiskFileItemFactory(); factory.setSizeThreshold(1024 * 1024); ServletFileUpload upload new ServletFileUpload(factory); upload.setFileSizeMax(5 * 1024 * 1024); // 限制单张图片5MB try { ListFileItem items upload.parseRequest(request); String title null, desc null; String originalPrice null, sellPrice null, categoryId null; String savedFileName null; for (FileItem item : items) { if (item.isFormField()) { switch (item.getFieldName()) { case title: title item.getString(UTF-8); break; case description: desc item.getString(UTF-8); break; case originalPrice: originalPrice item.getString(UTF-8); break; case sellPrice: sellPrice item.getString(UTF-8); break; case categoryId: categoryId item.getString(UTF-8); break; } } else { String fileName new File(item.getName()).getName(); String ext fileName.contains(.) ? fileName.substring(fileName.lastIndexOf(.)) : ; savedFileName System.currentTimeMillis() ext; String savePath getServletContext().getRealPath(/upload); File dir new File(savePath); if (!dir.exists()) dir.mkdirs(); item.write(new File(savePath, savedFileName)); } } GoodsService goodsService new GoodsService(); goodsService.publish(loginUser.getId(), Integer.parseInt(categoryId), title, desc, new BigDecimal(originalPrice), new BigDecimal(sellPrice), /upload/ savedFileName); response.sendRedirect(request.getContextPath() /index.jsp); } catch (Exception e) { e.printStackTrace(); request.setAttribute(errorMsg, 发布失败请检查图片大小或格式); request.getRequestDispatcher(/goods/publish.jsp).forward(request, response); } } }代码里的关键逻辑在于文件名重命名用System.currentTimeMillis()拼原始扩展名避免用户上传两个同名文件互相覆盖。item.write本质上把临时文件写到WebContent/upload/。还有一点容易被忽略——diskFileItemFactory.setSizeThreshold(1024*1024)只是内存阈值超过1MB会写入临时文件而setFileSizeMax(5*1024*1024)才是硬限制。3.4 列表页用JSTL而不是脚本片段拿到一套课程设计源码很多老代码的index.jsp长这样% for (Goods g : goodsList) { %在页面里写Java代码。这个写法能用但在现在IDEA里会画一堆黄色下划线答辩时印象分也会打个折扣。我更建议改成JSTLEL% taglib prefixc urihttp://java.sun.com/jsp/jstl/core % % taglib prefixfmt urihttp://java.sun.com/jsp/jstl/fmt % c:forEach varg items${goodsList} div classcard img src${g.image} alt${g.title} / h3${g.title}/h3 p classpricefmt:formatNumber value${g.sellPrice} pattern0.00//p a href${pageContext.request.contextPath}/goods/detail?id${g.id}查看详情/a /div /c:forEach这里注意${g.image}是相对路径/upload/xxx.jpg浏览器会拿它拼上当前域名直接访问。如果你的项目部署上下文是campus-trader那这张图片的完整URL就是http://localhost:8080/campus-trader/upload/xxx.jpg。Servlet在取图片时会去getServletContext().getRealPath(/upload)找文件两者路径必须严格对应。4. 把系统跑起来JDK、Tomcat、MySQL的配置顺序与参数细节4.1 版本匹配是第一道门槛拿到源码后先别急着导入IDE先确认三个版本JDK版本、Tomcat版本、MySQL驱动版本。这个三元组只要有一个对不上系统就会在启动或首次查询时报莫名其妙的老虎。课程设计源码多见于JDK 1.8 Tomcat 8.5/9.0 MySQL 5.7/8.0这是目前最稳的组合。如果你用的是JDK 17Tomcat 9也能跑但很多老源码里的javax.servlet依赖和JDK模块化会产生额外配置成本。排查顺序我一般是这样先看web.xml头部的version再看pom.xml或WEB-INF/lib下的mysql-connector-java文件。有些源码不带lib目录驱动jar需要自己补那就要记住一个原则MySQL 5.7用mysql-connector-java-5.1.49.jarMySQL 8.0用mysql-connector-java-8.0.33.jar。版本差一次启动时出现的报错完全不同。4.2 数据库连接URL里的必调参数JDBC连接串里除了地址、账号、密码还有三个参数直接影响中文和时区行为// DBUtil.java 核心代码 String url jdbc:mysql://localhost:3306/campus_trade?useUnicodetruecharacterEncodingUTF-8serverTimezoneAsia/ShanghaiuseSSLfalseallowPublicKeyRetrievaltrue; String user root; String password 你的密码; Class.forName(com.mysql.cj.jdbc.Driver); Connection conn DriverManager.getConnection(url, user, password);MySQL 8.0的驱动类名是com.mysql.cj.jdbc.Driver5.x是com.mysql.jdbc.Driver。如果你的工程用的8.x驱动但写了旧类名启动后第一次查库会报ClassNotFoundException。serverTimezoneAsia/Shanghai是8.0的必填项否则还会插入时间字段时报The server time zone value Öйú±ê׼ʱ¼ä is unrecognized。allowPublicKeyRetrievaltrue也是8.0特有的不设的话连接时可能遇到Public Key Retrieval is not allowed。这三行几乎是我排查JSP连MySQL问题的标准开头。4.3 IDE里部署Web工程的三个参数用IDEA的话配置项集中在Run/Debug Configurations - Tomcat Server下的Deployment选项卡。课程设计里最常见的坑是Web应用上下文路径对不上导致图片和CSS全挂。我的习惯是统一设置Application server 指向本机Tomcat安装目录URL 端口保持8080Deployment 里 Deploy at the host root上下文保持默认应用名比如campus-trader然后所有页面里的相对路径都写成${pageContext.request.contextPath}/...为什么强调这个因为JSP里如果写死/upload/xxx.jpg部署上下文不是ROOT时浏览器会去http://localhost:8080/upload/xxx.jpg找文件而Tomcat的webapps/upload是空的系统里所有图片都会裂开。用${pageContext.request.contextPath}才是动态适配上下文的正解。4.4 MySQL安装与初始化5.7与8.0的选择做这个系统时我用过MySQL 5.7和8.0两种环境结论是如果你的源码驱动是5.x直接装MySQL 5.7最省事。装8.0的话务必把驱动也换成8.x并设置好时区参数。Windows下安装MySQL时安装向导里“Choose a Setup Type”我一般选Developer Default但要注意端口3306有没有被占用。如果之前装过旧MySQL先清干净C:\ProgramData\MySQL残留目录否则新版服务会启动失败。安装完成后两个必做动作# 检查MySQL服务状态Windows net start | findstr -i mysql # 如果服务没启动以管理员身份执行 net start mysql # 命令行登录测试 mysql -u root -p登录后执行SHOW DATABASES;看是否存在campus_trade。如果没有把第2章的建库脚本整个粘贴进去执行。这一步很多人会在IDE里做结果连接失败时第一反应是代码问题最后发现库根本没建。5. 避坑与排查校园二手物品交易系统从部署到运行的常见问题5.1 登录时报500驱动类找不到或数据库连不上现象输入学号密码点登录页面直接跳出HTTP Status 500堆栈第一行是java.sql.SQLException: No suitable driver found for jdbc:mysql://localhost:3306/campus_trade。原因MySQL驱动jar包没有放到Web应用类路径下。常见做法是把jar复制到WebContent/WEB-INF/lib但有时在IDEA里没刷新依赖或者用的是Maven但pom.xml没有重新导入。解决先确认WEB-INF/lib下存在mysql-connector-java-*.jar如果存在检查Build Path里是否把它添加为Library。对不熟悉Maven的建议直接手动复制jar到lib目录这样最不容易出错。5.2 商品图片上传后访问404现象发布商品时图片上传成功后台upload/目录下也有文件但浏览器打开商品详情时图片裂开控制台404。原因JSP页面里写死了/upload/xxx.jpg未拼接应用的上下文路径。部署上下文不是根路径时真实访问URL是/上下文名/upload/xxx.jpg。另外还有个坑是Tomcat的conf/web.xml里对/upload目录的映射规则如果你的工程在根路径部署直接访问/upload可能被DefaultServlet处理但文件存在性检查失败。解决页面统一使用${pageContext.request.contextPath}/upload/xxx.jpg后端存/upload/xxx.jpg前端取时拼上下文。5.3 POST提交中文乱码现象登录成功后网页右上角用户名显示成一串问号发布商品描述也乱且数据库表里直接存的是乱码。原因JSP页面Content-Type没设置UTF-8Servlet也忘了调request.setCharacterEncoding(UTF-8)。更隐蔽的原因是Tomcat 8以上GET请求默认UTF-8但POST就看你过滤器。解决写一个编码过滤器拦截所有请求在doFilter里统一执行request.setCharacterEncoding(UTF-8); response.setCharacterEncoding(UTF-8);把这个过滤器配置到web.xml的filter-mapping里URL匹配/*。保证所有Servlet都先过这一层。5.4 商品列表一片空白控制台没有异常现象首页能打开但c:forEach渲染的区域是空的整个div高度为0等会儿又能看到数据闪现某些浏览器直接空白。原因最常见是Servlet在doGet里查了数据但没有将其放入request或session作用域。或者是JSP页面没有声明JSTL的taglib报错被isELIgnored吞掉。老的Tomcat版本默认isELIgnoredfalse但某些IDE生成的web.xml会开启isELIgnored。解决把查询代码贴过来看清楚。参考做法是GoodsService goodsService new GoodsService(); ListGoods goodsList goodsService.findAllOnSale(); request.setAttribute(goodsList, goodsList); request.getRequestDispatcher(/index.jsp).forward(request, response);然后JSP头里加% page isELIgnoredfalse %并引入两个taglib。5.5 后台管理登录不进去管理员表是空的现象前台用户注册登录都正常但用admin/admin123后台登录时提示“用户名或密码错误”。原因两张表——前台是tb_user后台是tb_admin。很多源码包自带的SQL脚本里没有插入管理员初始数据只建了表结构。你查tb_admin会发现一条记录都没有。解决手动向tb_admin插入一条MD5后的密码记录。MD5值为21232f297a57a5a743894a0e4a801fc3。如果登录逻辑里用SHA-256则改成SHA-256的哈希。注意看源码里AdminService的加密算法到底用的哪种。6. 让毕业设计看起来像成品三个不用加代码量的验证与加分点6.1 过滤器统一编码与登录拦截第一个加分项就是第5.3节提到的编码过滤器。它还能顺手做登录拦截——在doFilter里判断session.getAttribute(loginUser)是否为空为空则跳回登录页。这个功能只有十几行代码但比写一百行权限判断更容易让老师看到你有安全意识。建议在web.xml里配置两个过滤映射一个encodingFilter拦所有路径一个loginFilter只拦/goods/publish和/user/center等需要登录的路径比如filter filter-nameloginFilter/filter-name filter-classcom.campus.filter.LoginFilter/filter-class /filter filter-mapping filter-nameloginFilter/filter-name url-pattern/goods/publish/*/url-pattern url-pattern/user/center/*/url-pattern /filter-mapping这样未登录用户直接访问发布页面会被强制拉回登录页在演示时这个行为是实打实能看到的。6.2 验证链路用一条SQL确认系统闭环系统做完我习惯在MySQL命令行里做一次完整链路验证而不是光靠UI点点点。发一条SQLSELECT g.title AS 商品, u.username AS 卖家, o.price AS 成交价, o.create_time AS 成交时间, o.status AS 订单状态 FROM tb_order o JOIN tb_goods g ON o.goods_id g.id JOIN tb_user u ON o.seller_id u.id ORDER BY o.create_time DESC;这条SQL能查出订单表是否通过外键正确关联了商品和用户。如果这张表查出来为空说明你的代码里下单逻辑没有真正插入订单数据——无论界面弹了什么成功提示这条查询才是最诚实的。课程设计答辩时老师最爱问“为什么订单表里看不到刚才那条操作”所以这个验证步骤值得在演示前跑一遍。6.3 用日志反查一个“说不清”的Bug第三个技巧是遇到诡异Bug时别只盯着Tomcat控制台而是关注三层日志JSP编译日志、Servlet异常堆栈、MySQL的general_log。MySQL开启general有代价但在课设调试场景没关系可以在my.ini里临时打开general_logON general_log_fileC:/ProgramData/MySQL/MySQL Server 8.0/Data/general.log然后重复一次出错的操作打开日志文件能看到MySQL实际收到和执行了哪条SQL。很多时候界面报错是Java层变量拼接了非法SQL而这条明细能让问题暴露出来的速度翻倍。调完记得把general_log关掉否则日志文件会快速增长。我自己做这套系统时被坑得最惨的一次就是图片404——当时查了半小时代码最后发现是Tomcat部署名多了一层版本号所有路径全差一个目录。从那之后我每次部署Web工程都先看一遍URL栏里的上下文路径再决定是不是要去查代码。做校园二手物品交易这种经典课设代码本身不难难的是把数据库、容器、驱动这三个黑匣子都调到同一个频道。只要你顺着这篇文章把表建好、请求模型理清、部署参数对一遍这套源码在你手里就能从“能跑”变成“敢上台演示”的状态。希望帮到你。本文还有配套的精品资源点击获取
返回列表