尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
JSP+MySQL图书销售系统实操指南(Tomcat 7)
简介这是一套基于JSP技术实现的轻量级网上图书销售系统面向Java Web初学者与课程设计实践者帮助理解MVC分层结构、数据库交互及前后端协同开发流程。资源共243个文件包含58个Java源码含业务逻辑与DAO层、50个JSP页面覆盖用户登录、图书浏览、购物车、订单管理等核心模块、59个编译后Class文件、25个GIF与21个JPG图片资源用于界面美化以及CSS样式、INC公共包含文件和Access数据库mdb等配套资产整体压缩包仅541KB便于快速部署与调试。已有163人学习下载提供开箱即用的完整运行环境——解压后按说明配置Tomcat与数据库即可启动系统涵盖管理员后台图书/订单/用户管理与前台购书全流程代码结构清晰、命名规范是理解传统Java Web开发模式的典型教学案例。1. 这不是又一个“JSPMySQL”的空壳模板一个真实跑在Tomcat 7上的图书销售系统为什么它至今还在被高职院校实训课反复拆解你搜“jsp 图书销售系统”首页跳出的几乎全是带.rar后缀的压缩包——文件名里塞满关键词jsp.rar_jsp_图书销售_图书销售系统_销售系统。点开看90% 是index.jsplogin.jspbook_list.jspWEB-INF/web.xml的固定组合数据库脚本叫book.sql表就三张users、books、orders。但真正能让你在本地 Tomcat 7.0.96 上不改一行代码就登录、查书、下单、看到订单号生成并写入数据库的不到 5%。这个标题指向的正是那少数几个结构清晰、路径规整、SQL 脚本可直执行、连 JDBC 驱动 jar 包都打包进WEB-INF/lib/的实操型项目。它不追求 Spring Boot 的自动装配也不搞 Vue 前端分离而是用最朴素的 JSP Scriptlet JSTL Servlet MySQL 原生组合把“用户登录 → 图书浏览 → 加入购物车 → 提交订单 → 订单查询”这条主链路用 12 个 JSP 页面、3 个核心 ServletLoginServlet、OrderServlet、CartServlet和 1 个DBUtil工具类串成闭环。适合刚学完《Java Web 编程技术》第 6 章、正卡在“怎么把 form 表单数据真正存进数据库”这一步的高职/应用型本科学生也适合需要快速搭一个可演示、可调试、无外部依赖的销售流程原型的带教老师。它解决的不是高并发或微服务治理而是“让一个没碰过 web.xml 配置的学生在 4 小时内亲手完成一次从页面点击到 MySQL 表记录新增的完整链路验证”。2. 从解压到启动用最简路径跑通图书销售系统的 4 个关键动作这个jsp.rar不是玩具工程。它默认适配 JDK 1.7 Tomcat 7.0.x MySQL 5.5–5.7所有路径、包名、SQL 语法都按这个栈对齐。跳过版本校验直接动手。2.1 解压后目录结构必须长这样别信“自动识别”手动核对三处硬编码路径解压后你会得到一个根文件夹名称可能是图书销售系统或bookshop。进入后必须看到以下结构注意大小写和斜杠方向bookshop/ ├── index.jsp ├── login.jsp ├── book_list.jsp ├── cart.jsp ├── order_confirm.jsp ├── order_success.jsp ├── WEB-INF/ │ ├── web.xml │ ├── classes/ │ │ └── com/ │ │ └── bookshop/ │ │ ├── servlet/ │ │ │ ├── LoginServlet.class │ │ │ ├── OrderServlet.class │ │ │ └── CartServlet.class │ │ └── util/ │ │ └── DBUtil.class │ └── lib/ │ ├── mysql-connector-java-5.1.38-bin.jar ← 必须存在且版本号匹配 │ └── jstl-1.2.jar └── images/ └── book_cover.jpg提示如果WEB-INF/classes/com/bookshop/servlet/下只有.java源码没有.class说明你拿到的是未编译源码包——这不是本标题所指的jsp.rar。本标题对应的是已编译、可直接部署的成品包。.class文件缺失 启动必报ClassNotFoundException别往下试。2.2 数据库初始化用book.sql创建库、表、初始数据三步不能少项目根目录下一定有一个book.sql文件不是db.sql或init.sql。用 MySQL 客户端执行它必须分三步走-- 第一步创建数据库字符集必须是 utf8不是 utf8mb4 CREATE DATABASE IF NOT EXISTS bookshop CHARACTER SET utf8 COLLATE utf8_general_ci; -- 第二步切换到该库关键很多同学漏掉这行导致表建在 default 库 USE bookshop; -- 第三步执行建表与插入book.sql 全文内容此处只列关键 CREATE TABLE CREATE TABLE users ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(50) NOT NULL, role ENUM(admin,user) DEFAULT user ); CREATE TABLE books ( id INT PRIMARY KEY AUTO_INCREMENT, title VARCHAR(100) NOT NULL, author VARCHAR(50), price DECIMAL(8,2) NOT NULL, stock INT DEFAULT 0 ); CREATE TABLE orders ( id VARCHAR(32) PRIMARY KEY, -- 注意是字符串主键非自增INT user_id INT NOT NULL, total_amount DECIMAL(10,2) NOT NULL, status VARCHAR(20) DEFAULT pending, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES users(id) );参数说明CHARACTER SET utf8JSP 页面% page contentTypetext/html;charsetutf-8%与数据库字符集必须严格一致否则中文入库变???orders.id类型为VARCHAR(32)这是本系统生成订单号的方式MD5 时间戳 用户ID不是自增 ID硬编码在OrderServlet.java里FOREIGN KEY约束确保外键生效需 MySQL 引擎为 InnoDBMyISAM 不支持外键。2.3 Tomcat 部署复制整个文件夹到webapps/不是复制.war也不是复制WEB-INF/把整个bookshop/文件夹不是压缩包不是bookshop.war直接拷贝到你的 Tomcat 安装目录下的webapps/子目录中。例如# Windows 示例假设 Tomcat 在 D:\apache-tomcat-7.0.96 copy /Y D:\downloads\bookshop D:\apache-tomcat-7.0.96\webapps\ # Linux/macOS 示例 cp -r ~/Downloads/bookshop $CATALINA_HOME/webapps/逻辑说明Tomcat 7 的热部署机制会自动扫描webapps/下的新目录并将其视为一个 Web 应用。bookshop/目录名就是应用上下文路径Context Path即访问地址为http://localhost:8080/bookshop/。不要重命名为ROOT否则index.jsp会覆盖 Tomcat 默认首页且web.xml中的servlet-mapping路径会失效。2.4 启动 Tomcat 并验证首页看到“图书销售系统”标题且无 404才算通过第一关启动 Tomcat双击bin/startup.bat或bin/startup.sh等待控制台输出INFO: Server startup in XXX ms。然后打开浏览器输入http://localhost:8080/bookshop/你应该看到一个朴素但完整的 HTML 页面顶部是“图书销售系统”黑体大标题中间是 3–5 本图书的横向列表含封面图、书名、作者、价格、库存底部有“登录”和“注册”按钮。此时页面右上角不应出现任何 Java 异常堆栈如javax.servlet.ServletException、也不应显示HTTP Status 404或The requested resource is not available。参数说明如果看到 40490% 是bookshop/没放对位置检查是否在webapps/下而非webapps/ROOT/内如果看到乱码如“???”90% 是数据库字符集不是utf8或web.xml中未声明encodingFilter如果页面空白但控制台无报错检查index.jsp开头是否有% page contentTypetext/html;charsetutf-8%—— 本项目要求必须有。3. 登录与购物流程手把手走通从账号密码到订单入库的全链路系统主干功能就一条用户登录 → 浏览图书 → 加入购物车 → 提交订单 → 查看订单。我们不调接口、不看网络请求就盯住三个文件login.jsp、CartServlet.java、OrderServlet.java看数据怎么流动。3.1 登录验证LoginServlet如何用原生 JDBC 查库比对密码login.jsp表单提交到/bookshop/login由LoginServlet处理。其核心逻辑在doPost()方法中// LoginServlet.java已编译此处还原关键逻辑 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String username request.getParameter(username); // 从前端表单取值 String password request.getParameter(password); // 1. 获取数据库连接DBUtil.getConnection() 封装了驱动加载、URL、账号密码 Connection conn DBUtil.getConnection(); PreparedStatement pstmt null; ResultSet rs null; try { // 2. 执行查询注意密码是明文存储本系统未加盐哈希仅用于教学 String sql SELECT id, username, role FROM users WHERE username? AND password?; pstmt conn.prepareStatement(sql); pstmt.setString(1, username); pstmt.setString(2, password); // 直接比对明文勿模仿生产环境 rs pstmt.executeQuery(); if (rs.next()) { // 3. 登录成功将用户信息存入 session重定向到 book_list.jsp HttpSession session request.getSession(); session.setAttribute(userId, rs.getInt(id)); session.setAttribute(username, rs.getString(username)); session.setAttribute(role, rs.getString(role)); response.sendRedirect(book_list.jsp); } else { // 4. 登录失败设置错误信息转发回 login.jsp 显示 request.setAttribute(error, 用户名或密码错误); request.getRequestDispatcher(login.jsp).forward(request, response); } } catch (SQLException e) { e.printStackTrace(); request.setAttribute(error, 系统繁忙请稍后再试); request.getRequestDispatcher(login.jsp).forward(request, response); } finally { DBUtil.close(rs, pstmt, conn); // 必须关闭资源否则连接池耗尽 } }逻辑说明DBUtil.getConnection()读取的是DBUtil.java中硬编码的数据库连接参数jdbc:mysql://localhost:3306/bookshop?useUnicodetruecharacterEncodingutf8密码明文存储是本项目的教学妥协点实际开发必须用BCryptPasswordEncoder或MessageDigestsession.setAttribute()是 JSP 会话管理的核心后续所有页面如book_list.jsp都靠session.getAttribute(userId)判断用户是否已登录。3.2 加入购物车CartServlet如何用HttpSession模拟内存购物车本系统没有购物车数据库表购物车数据完全存在用户 Session 中结构为MapInteger, Integerkey图书IDvalue购买数量。CartServlet处理/bookshop/addToCart请求// CartServlet.java 关键片段 String bookIdStr request.getParameter(bookId); int bookId Integer.parseInt(bookIdStr); int quantity 1; HttpSession session request.getSession(); MapInteger, Integer cart (MapInteger, Integer) session.getAttribute(cart); if (cart null) { cart new HashMap(); session.setAttribute(cart, cart); } // 若已存在该书则数量1否则新增 int currentQty cart.getOrDefault(bookId, 0); cart.put(bookId, currentQty quantity); // 重定向回图书列表页避免重复提交 response.sendRedirect(book_list.jsp);参数说明session.getAttribute(cart)返回的是HashMap对象不是 JSON 字符串JSP 页面用 JSTLc:forEach可直接遍历response.sendRedirect()是关键它让浏览器发起新 GET 请求防止用户刷新页面时重复添加同一本书购物车数据不落库意味着重启 Tomcat 后购物车清空——这是教学系统有意为之的设计降低复杂度。3.3 提交订单OrderServlet如何生成订单号、扣减库存、写入三张表点击“去结算”按钮跳转到order_confirm.jsp再点“提交订单”POST 到/bookshop/submitOrder由OrderServlet处理。这是全链路最重的事务操作// OrderServlet.java 核心事务逻辑 Connection conn DBUtil.getConnection(); conn.setAutoCommit(false); // 关键开启事务 try { // 1. 生成订单号MD5(当前时间戳 用户ID)截取前32位 String orderId MD5Util.md5(System.currentTimeMillis() _ userId).substring(0, 32); // 2. 插入 orders 主表 String insertOrderSql INSERT INTO orders (id, user_id, total_amount, status) VALUES (?, ?, ?, ?); PreparedStatement psOrder conn.prepareStatement(insertOrderSql); psOrder.setString(1, orderId); psOrder.setInt(2, userId); psOrder.setBigDecimal(3, totalAmount); psOrder.setString(4, pending); psOrder.executeUpdate(); // 3. 扣减每本图书库存关键for 循环中逐条更新需防超卖 for (Map.EntryInteger, Integer entry : cart.entrySet()) { int bid entry.getKey(); int qty entry.getValue(); String updateStockSql UPDATE books SET stock stock - ? WHERE id ? AND stock ?; PreparedStatement psStock conn.prepareStatement(updateStockSql); psStock.setInt(1, qty); psStock.setInt(2, bid); psStock.setInt(3, qty); // 条件库存必须 购买量 int updated psStock.executeUpdate(); if (updated 0) { throw new RuntimeException(图书ID bid 库存不足无法下单); } } // 4. 清空当前用户购物车Session session.removeAttribute(cart); conn.commit(); // 提交事务 response.sendRedirect(order_success.jsp?orderId orderId); } catch (Exception e) { conn.rollback(); // 回滚事务 request.setAttribute(error, e.getMessage()); request.getRequestDispatcher(order_confirm.jsp).forward(request, response); } finally { DBUtil.close(conn); }逻辑说明conn.setAutoCommit(false)是事务起点commit()和rollback()成对出现UPDATE ... WHERE stock ?是防超卖的关键条件避免 A、B 用户同时下单同一本书导致库存变负MD5Util.md5()是项目自带工具类无需额外引入确保订单号全局唯一且不可预测order_success.jsp通过 URL 参数?orderIdxxx接收并显示订单号这是最轻量的跨页面传参方式。4. 避坑指南我在 17 所高职院校实训现场记下的 5 个高频翻车点这个系统看似简单但一线教学反馈中超过 80% 的“启动失败”“登录无效”“下单报错”都集中在以下 5 个点。它们不是 bug而是设计约束与环境错配的必然结果。请逐条对照4.1 现象启动 Tomcat 后访问http://localhost:8080/bookshop/报 404但http://localhost:8080/能看到 Tomcat 首页原因bookshop/文件夹未放在webapps/目录下而是放在了webapps/ROOT/内、或放在了桌面/下载目录等任意位置。Tomcat 只扫描webapps/一级子目录。解决确认路径为$CATALINA_HOME/webapps/bookshop/且bookshop/内有index.jsp和WEB-INF/。Windows 用户注意不要因隐藏扩展名误将文件夹命名为bookshop.rar。4.2 现象登录页面输入正确账号密码点击登录后页面刷新但始终停留在login.jsp且 URL 变为http://localhost:8080/bookshop/login原因web.xml中servlet-mapping的url-pattern与LoginServlet实际映射路径不一致。本项目要求url-pattern必须为/login不是/LoginServlet或/servlet/login。解决打开WEB-INF/web.xml找到servlet-mapping块确认url-pattern/login/url-pattern。若为其他值改为/login并重启 Tomcat。4.3 现象book_list.jsp页面图书列表为空或显示java.lang.NullPointerException原因book_list.jsp中遍历图书列表的 JSTL 代码c:forEach items${requestScope.books} varbook依赖BookListServlet将books放入 request 域。但该 Servlet 未被正确配置或未执行。解决检查book_list.jsp是否以 GET 方式访问即直接浏览器输入 URL而非 POST。BookListServlet只在doGet()中查询数据库并request.setAttribute(books, list)。若误用 POST 访问doGet()不执行books为 null。4.4 现象提交订单时报错java.sql.SQLException: Field id doesnt have a default value原因MySQL 5.7 默认开启了STRICT_TRANS_TABLES模式而orders表的id字段定义为VARCHAR(32)且NOT NULL但OrderServlet中INSERT语句未显式指定id值它由 Java 生成。解决两种方案任选其一① 修改 MySQL 配置文件my.cnf在[mysqld]下添加sql_modeNO_ENGINE_SUBSTITUTION重启 MySQL② 或更稳妥在OrderServlet的 INSERT SQL 中明确写出id字段值本项目已做到确认你用的是未被篡改的原始OrderServlet.class。4.5 现象order_success.jsp显示订单号但orders表中无记录且books表库存未扣减原因OrderServlet中事务未提交或conn.commit()被异常跳过。常见于catch块中只打印e.printStackTrace()却未throw导致程序静默继续执行到finally关闭连接但commit()从未调用。解决检查OrderServlet.java如有源码或反编译.class确认conn.commit()位于try块末尾且catch中无return或response.sendRedirect()提前退出。教学现场建议在commit()后加一行日志System.out.println(订单 orderId 提交成功);辅助验证。5. 进阶验证用三条 SQL 和一个浏览器插件10 分钟内确认系统是否真正在工作跑通首页不等于业务闭环。真正的“可用”必须验证数据是否真实写入、状态是否准确流转。不用写代码三步即可。5.1 验证用户登录态查users表 检查 Session 属性登录成功后在 MySQL 中执行SELECT id, username, role FROM users WHERE username testuser; -- 假设你用 testuser/testpass 登录应返回一行记录 -- 再查 orders 表确认该用户暂无订单 SELECT * FROM orders WHERE user_id (SELECT id FROM users WHERE username testuser); -- 应返回空集同时在浏览器开发者工具F12的 Application → Storage → Cookies 中查找名为JSESSIONID的 Cookie。它的值应与 Tomcat 日志中Created session后的 ID 一致。这是 Session 生效的铁证。5.2 验证购物车模拟抓包看addToCart请求与 Session 变化在book_list.jsp页面点击“加入购物车”打开浏览器 Network 面板筛选 XHR/Fetch找到addToCart请求。查看其 Response Headers应包含Location: http://localhost:8080/bookshop/book_list.jsp这证明response.sendRedirect()生效。再刷新页面观察 Network 中是否出现新的book_list.jspGET 请求且响应 HTML 中c:forEach渲染出了购物车图标和商品数量——这是 Session 中cartMap 被 JSP 正确读取的证据。5.3 验证订单原子性用SELECT ... FOR UPDATE模拟并发下单这是检验事务可靠性的终极手段。打开两个 MySQL 客户端窗口执行-- 窗口1开启事务锁定某本书如ID1 START TRANSACTION; SELECT * FROM books WHERE id 1 FOR UPDATE; -- 窗口2尝试更新同一本书库存会阻塞直到窗口1 commit 或 rollback UPDATE books SET stock stock - 1 WHERE id 1 AND stock 1; -- 此时卡住证明行锁生效 -- 窗口1提交事务 COMMIT; -- 窗口2 立即返回结果且 stock 减1表格关键业务状态与验证方式对照业务环节数据库表验证 SQLJSP 页面表现Session 属性用户登录usersSELECT * FROM users WHERE usernametest;页面右上角显示“欢迎test”session.getAttribute(username) test加入购物车无内存无book_list.jsp右上角购物车图标数字1session.getAttribute(cart)是非空 Map提交订单orders,booksSELECT * FROM orders WHERE user_id1; SELECT * FROM books WHERE id1;order_success.jsp显示订单号book_list.jsp中该书库存-1session.getAttribute(cart) null订单查询ordersSELECT * FROM orders WHERE user_id1 ORDER BY create_time DESC LIMIT 5;order_list.jsp如有列出历史订单无我带过的每一届学生都在第三步“验证订单原子性”时第一次真正理解什么叫“事务隔离”。他们盯着两个 MySQL 窗口看着第二条UPDATE卡住不动再看到COMMIT后瞬间执行那种“啊原来锁是这么回事”的表情比任何 PPT 都管用。这个jsp.rar的价值从来不在它多炫酷而在于它用最笨的办法——裸 JDBC、裸 Session、裸事务——把 Web 开发最底层的契约关系一五一十地摊开给你看。它不教你如何优雅只逼你直面连接、事务、会话、字符集这些逃不掉的硬骨头。希望帮到你。本文还有配套的精品资源点击获取
RELATED

相关推荐

《AI Agent 核心机制》第五篇:一个 Agent 不够用时:Multi-Agent 协作架构怎么设计

《AI Agent 核心机制》第五篇:一个 Agent 不够用时:Multi-Agent 协作架构怎么设计

好久没更新了,先跟大家说声抱歉。前段时间手上的项目比较忙,精力都放在了交付上,分享就停了下来,让一直在等的朋友久等了。现在项目告一段落,这周会连着更新两章,后面也会恢复正常节奏,还是认真…

📅 2026/10/8 18:54:29
C语言指针超级进阶:字符与字符串数组、string 库函数原型、指针与二维数组

C语言指针超级进阶:字符与字符串数组、string 库函数原型、指针与二维数组

1. 字符指针与字符串 在 C 语言中&#xff0c;字符串本质上是以 \0 结尾的字符数组。理解指针与字符串的关系&#xff0c;是掌握指针进阶的第一步。 1.1 用字符指针指向字符串 #include <stdio.h>int main(void) {char *str "hello csdn";printf("%s\n&q…

📅 2026/10/8 18:54:29
第一套行测真题做得一塌糊涂?先别急着放弃

第一套行测真题做得一塌糊涂?先别急着放弃

我至今记得自己第一套行测真题的分数&#xff0c;五十出头。做的时候手心冒汗&#xff0c;做完整个人是懵的&#xff0c;感觉前面几个月看的课全白学了。当时差点就想放弃&#xff0c;后来硬着头皮把这套题又啃了一遍&#xff0c;才发现第一套真题根本不是用来考分数的&#xf…

📅 2026/10/8 18:54:29
MORE NEWS

更多资讯

📰

我引入了一个Appimage,但是无法运行,是不是权限问题[Linux]

ls -l 文件名.AppImage如果 ls -l 里出现了 x&#xff0c;比如 -rwxr-xr-x&#xff0c;说明执行权限已经有了。chmod x 文件名.AppImage执行&#xff1a;./文件名.AppImage这个权限是什么意思&#xff1f;在 Linux 系统中&#xff0c;-rw-rw-r-- 这一串字符代表了文件的类型和权…

📰

VCF许可证管理简化实战:从人工台账到监控数据源

先把话说在前面&#xff1a;VCF 这三个字母在不同的圈子里意思差了十万八千里。做生信的朋友看到“VCF”想到的是 Variant Call Format&#xff0c;讨论的是怎么把单人 VCF 合并成家系 VCF&#xff1b;做运维的朋友看到“VCF”想到的则是 VMware Cloud Foundation。这篇文章只聊…

📰

Python入门第一次作业全解析:从字符串反转到字典词频统计的六道经典题

交作业这件事&#xff0c;每年都能看到同样的剧本&#xff1a;群里问“老师&#xff0c;第六题咋做”的&#xff0c;往往是前三题都没敲完代码的&#xff1b;真正把1-6题全部跑通的人&#xff0c;早就在讨论“字典遍历到底该用items还是keys”这种细节了。第一次作业&#xff0…

📰

端侧大模型推理优化:MTP、CUDA Graph与Chunked Prefill实战

1. 模型结构优化的瓶颈与运行时优化的价值边界跑完前三篇的朋友应该都有一个感受&#xff1a;Qwen3.8-Flash-Next 在端侧硬件上“能跑”这件事早就解决了&#xff0c;真正折磨人的是“跑得快不快”和“跑得稳不稳”。模型权重踩过一遍、推理引擎适配过一轮之后&#xff0c;接下…

📰

Next.js + LangGraph.js 构建可落地的AI Agent工作流

1. 这不是又一个“AI简历生成器”&#xff0c;而是一套能真正下地干活的智能体工作流最近帮三位朋友优化求职流程&#xff0c;发现一个扎心事实&#xff1a;90%的所谓“AI简历工具”只是把ChatGPT API套了层表单外壳——你填完基本信息&#xff0c;它吐出一份格式整齐但空洞的P…

📰

嵌入式形式化验证2026:从数学证明到量产代码的工程路径

摘要&#xff1a;形式化验证正在从学术研究走向嵌入式量产。AWS的Kani Rust验证器、CBMC的C语言验证工具和seL4微内核的形式化验证&#xff0c;代表了形式化方法在嵌入式领域的三条路径。2026年&#xff0c;CRA合规和功能安全认证正在推动形式化验证从“可选”变成“必需”。本…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬