ARTICLE DETAIL

资讯详情

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

JavaWeb MVC实战:JSP+Servlet+MySQL购书系统

JavaWeb MVC实战:JSP+Servlet+MySQL购书系统 简介这是一份面向JavaWeb初学者与高校计算机专业学生的期末大作业级实战项目完整实现基于JSPServletMySQL的在线购书系统严格遵循MVC三层架构设计覆盖用户注册登录、图书浏览/搜索、购物车管理、订单提交与后台图书管理等核心电商功能兼顾界面美观性与操作便捷性特别适合作为课程设计或高分结课项目。资源包共75个文件含14个JSP页面前端交互、12个Java类Controller与Service逻辑、12个Class字节码编译验证、3个SQL脚本含完整数据库建表与初始化数据、3个XML配置文件web.xml及框架配置、16张界面截图与1个项目演示MP4视频整体压缩包大小为47.77MB。已有349人学习下载资源代码注释详尽、结构清晰、部署简易附带README说明与项目展示视频新手可快速理解模块职责与调用流程是深入掌握JavaWeb开发全流程的优质实践范例。1. 这不是“抄作业”是能跑通的 MVC 实战闭环一个带完整数据库、注释拉满、IDEA 可一键部署的 JSPServletMySQL 在线购书系统你交的 JavaWeb 期末大作业是不是还在用index.jsp里硬写 SQL、% %里拼 HTML、登录校验靠 session.setAttribute(user, admin) 然后全项目到处 getAttribute别慌——这个「在线购书系统」不是模板套壳它是一套真实跑在 Tomcat 上、三层职责清晰、DAO 层封装了 Connection 获取与关闭、Service 层做了事务控制、Controller 层严格遵循 Servlet 规范、JSP 页面只做展示不掺逻辑的可调试、可打断点、可改需求的 MVC 实战样本。它解决的不是“怎么交差”而是“怎么让老师一眼看出你真懂 MVC 分层价值”比如用户下单时库存扣减和订单生成必须原子性它用Connection.setAutoCommit(false)try-catch-rollback实现比如图书列表分页它没用前端 JS 模拟而是BookService.listByPage(pageNum, pageSize)真查数据库比如管理员后台的图书增删改每个操作都有独立 Servlet 处理URL 路由清晰/admin/addBook,/admin/deleteBook?id123。适合刚学完 JDBC 和 Servlet 生命周期、正卡在“知道 MVC 概念但写不出分层代码”的人——下载解压后按本文第 3 章配好 MySQL 和 Tomcatmvn clean compile或直接导入 IDEA→ 启动 → 访问http://localhost:8080/bookstore首页就弹出来不用改一行代码。这不是玩具 demo是老师打分时会圈出“DAO 层异常处理规范”“Servlet doGet/doPost 职责分离合理”的满分项目。2. 从零部署环境准备、数据库初始化与项目导入三步落地2.1 环境清单与版本兼容性确认别跳这步这个项目基于传统 JavaWeb 技术栈不依赖 Maven 仓库自动拉包所有 jar 包已打包进WEB-INF/lib/目录。但环境版本必须对齐否则你会卡在ClassNotFoundException或NoClassDefFoundErrorJDK 版本必须为JDK 81.8。项目.classpath文件中明确指定classpathentry kindcon pathorg.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8/。若你本地是 JDK 17Tomcat 启动时会报Unsupported major.minor version 52.0这是 JDK 8 的 class 文件版本号必须切回 JDK 8。Tomcat 版本推荐Tomcat 8.5.x 或 9.0.x。项目web.xml中web-app声明为version3.1对应 Servlet 3.1 规范Tomcat 7 不支持其最高为 Servlet 3.0。MySQL 版本5.7.x 或 8.0.x 均可。SQL 文件mobiledatabase.sql使用标准 ANSI SQL无存储过程或窗口函数兼容性极强。但注意 MySQL 8.0 默认认证插件为caching_sha2_password而项目使用的mysql-connector-java-5.1.47.jar在WEB-INF/lib/下不支持该插件需手动修改 MySQL 用户认证方式见 2.2 节。提示不要用最新版 IDEA 直接“Open”项目根目录它会误判为普通文件夹。正确做法是选File → New → Project from Existing Sources...指向解压后的main文件夹即含.project和src的目录IDEA 会自动识别为 Eclipse Web Project。2.2 数据库初始化建库、建表、导入数据、修复 MySQL 8.0 认证问题项目提供mobiledatabase.sql但直接执行会失败——尤其在 MySQL 8.0 环境下。以下是安全初始化流程# 1. 登录 MySQL假设 root 密码为 123456 mysql -u root -p # 2. 创建数据库字符集必须为 utf8mb4避免中文乱码 CREATE DATABASE bookstore DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; # 3. 切换到新库 USE bookstore; # 4. 执行 SQL 文件注意路径要替换成你本地的实际路径 SOURCE /path/to/your/download/mobiledatabase.sql;如果执行SOURCE时卡住或报错ERROR 1045 (28000): Access denied for user rootlocalhost大概率是 MySQL 8.0 认证插件不兼容。解决方案仅针对 MySQL 8.0-- 登录 MySQL 后执行需 root 权限 ALTER USER rootlocalhost IDENTIFIED WITH mysql_native_password BY 123456; FLUSH PRIVILEGES;参数说明mysql_native_password是旧版认证插件mysql-connector-java-5.1.47.jar唯一支持的类型。FLUSH PRIVILEGES强制刷新权限缓存否则修改不生效。此操作不影响安全性仅解决连接兼容性。mobiledatabase.sql创建了 5 张核心表users用户账号密码明文存储教学项目非生产环境books图书信息ISBN、书名、作者、价格、库存orders订单主表订单号、用户ID、总金额、状态order_items订单明细关联订单与图书含购买数量admin_users管理员账号用于后台登录所有表均设定了ENGINEInnoDB和DEFAULT CHARSETutf8mb4确保事务支持与中文存储。2.3 IDEA 中导入并配置 Tomcat 运行导入项目File → New → Project from Existing Sources...→ 选择解压后的main文件夹 → 选中Import project from external model→ 选Eclipse→ Finish。配置 TomcatRun → Edit Configurations...→→Tomcat Server → LocalApplication server点击Configure...指向你的 Tomcat 安装目录如D:\apache-tomcat-8.5.99Deployment标签页 →→Artifact→ 选bookstore:war exploded项目名可能显示为bookstore或main认准war exploded类型Application context改为/bookstore这样访问地址就是http://localhost:8080/bookstore设置 JDKFile → Project Structure → Project→Project SDK选 JDK 1.8Project language level选8 - Lambdas, type annotations etc.启动点击绿色三角形 ▶️等待 Tomcat 启动日志出现INFO [main] org.apache.catalina.startup.Catalina.start Server startup in [xxx] milliseconds浏览器打开http://localhost:8080/bookstore即可见首页。注意首次启动时IDEA 会自动编译src下的 Java 文件到out/artifacts/bookstore_war_exploded/WEB-INF/classes/。若页面 404请检查Deployment中的Application context是否为/bookstore且Artifact是否选对war exploded。3. MVC 三层拆解看懂每一层在做什么、为什么这么分3.1 Controller 层Servlet 是路由中枢不是业务处理器项目中所有请求入口都是Servlet位于src/com/bookstore/servlet/下。以用户登录为例// src/com/bookstore/servlet/LoginServlet.java public class LoginServlet extends HttpServlet { private UserService userService new UserService(); // 依赖 Service 层 Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String username request.getParameter(username); String password request.getParameter(password); // 1. 调用 Service 层验证用户 User user userService.login(username, password); if (user ! null) { // 2. 登录成功存入 session重定向到首页 request.getSession().setAttribute(user, user); response.sendRedirect(request.getContextPath() /index.jsp); } else { // 3. 登录失败设置错误信息转发回登录页 request.setAttribute(msg, 用户名或密码错误); request.getRequestDispatcher(/login.jsp).forward(request, response); } } }关键设计点doPost只做三件事取参 → 调 Service → 控制跳转。绝不写 SQL、不操作数据库、不拼 HTML。request.getSession().setAttribute(user, user)将用户对象存入 Session供后续页面如header.jsp通过 EL 表达式${sessionScope.user.username}获取。response.sendRedirect(...)是客户端重定向URL 变request.getRequestDispatcher(...).forward(...)是服务器端转发URL 不变此处登录成功用重定向避免刷新重复提交。web.xml中的映射定义了 URL 路由servlet servlet-nameLoginServlet/servlet-name servlet-classcom.bookstore.servlet.LoginServlet/servlet-class /servlet servlet-mapping servlet-nameLoginServlet/servlet-name url-pattern/login/url-pattern !-- 访问 /login 即触发此 Servlet -- /servlet-mapping3.2 Service 层业务逻辑的“守门人”事务与规则在此集中src/com/bookstore/service/下的类是真正的业务大脑。以OrderService.placeOrder()为例// src/com/bookstore/service/OrderService.java public class OrderService { private OrderDao orderDao new OrderDao(); private BookDao bookDao new BookDao(); public boolean placeOrder(User user, ListOrderItem items) { Connection conn null; try { conn JdbcUtil.getConnection(); // 从工具类获取连接 conn.setAutoCommit(false); // 关闭自动提交开启事务 // 1. 保存订单主表 String orderId ORD System.currentTimeMillis(); // 简单生成订单号 Order order new Order(orderId, user.getId(), getTotalAmount(items), 待支付); orderDao.save(conn, order); // 2. 保存订单明细并扣减库存 for (OrderItem item : items) { orderDao.saveItem(conn, item); // 保存明细 bookDao.updateStock(conn, item.getBookId(), -item.getQuantity()); // 库存减 } conn.commit(); // 全部成功才提交 return true; } catch (SQLException e) { try { if (conn ! null) conn.rollback(); // 任一环节失败回滚 } catch (SQLException ex) { ex.printStackTrace(); } e.printStackTrace(); return false; } finally { JdbcUtil.close(conn); // 必须关闭连接 } } }为什么必须有 Service 层事务控制订单创建涉及多张表orders,order_items,books必须原子性。DAO 层只负责单表 CRUD事务边界在 Service 层划定。业务规则集中库存扣减逻辑bookDao.updateStock(...)与订单生成耦合若放在 Servlet 里下次加“库存不足时提示”就得改两个地方放在 Service 里只需改placeOrder()方法。复用性同一个placeOrder()方法既被OrderServlet调用也可被后台定时任务调用无需重复写逻辑。3.3 DAO 层数据库操作的“搬运工”屏蔽 JDBC 细节src/com/bookstore/dao/下的类只做一件事把 Java 对象和 SQL 语句来回转换。以BookDao为例// src/com/bookstore/dao/BookDao.java public class BookDao { public ListBook findAll(Connection conn) throws SQLException { String sql SELECT id, isbn, title, author, price, stock, image FROM books; PreparedStatement ps conn.prepareStatement(sql); ResultSet rs ps.executeQuery(); ListBook books new ArrayList(); while (rs.next()) { Book book new Book(); book.setId(rs.getInt(id)); book.setIsbn(rs.getString(isbn)); book.setTitle(rs.getString(title)); book.setAuthor(rs.getString(author)); book.setPrice(rs.getDouble(price)); book.setStock(rs.getInt(stock)); book.setImage(rs.getString(image)); books.add(book); } rs.close(); ps.close(); return books; // 返回纯 POJO 列表不含 Connection 或 ResultSet } }DAO 层的契约精神所有方法参数都接收Connection由调用方Service统一管理连接生命周期DAO 不自己getConnection()。所有方法返回值都是领域对象Book,User,Order或集合绝不返回ResultSet或PreparedStatement。异常只抛SQLException不捕获让 Service 层决定如何处理重试、回滚、记录日志。4. 避坑指南五个血泪经验总结专治部署失败、功能失灵、老师质疑4.1 现象启动 Tomcat 后访问http://localhost:8080/bookstore显示 404但控制台无报错原因IDEA 的 Artifact 配置未指向war exploded或Application context设置为空默认为/导致项目部署在根路径而index.jsp实际在/bookstore/下。解决Run → Edit Configurations...→Deployment标签页 → 确认Artifact是bookstore:war exploded不是bookstore:war且Application context明确填写/bookstore。4.2 现象登录时提示 “java.lang.ClassNotFoundException: com.mysql.jdbc.Driver”原因项目使用mysql-connector-java-5.1.47.jar其 Driver 类名为com.mysql.jdbc.Driver但 MySQL 8.0 的官方驱动类名已改为com.mysql.cj.jdbc.Driver。虽然 jar 包自带但若你手动替换了更高版本驱动就会报此错。解决删除WEB-INF/lib/下所有mysql-connector*.jar只保留项目自带的mysql-connector-java-5.1.47.jar。检查src/com/bookstore/util/JdbcUtil.java中Class.forName(com.mysql.jdbc.Driver);未被修改。4.3 现象图书列表页中文显示为??数据库里存的是乱码原因MySQL 服务端、数据库、表、连接四层字符集未统一为utf8mb4或 JDBC 连接 URL 缺少characterEncodingutf8mb4参数。解决确认 MySQL 配置文件my.iniWindows或my.cnfLinux中有[client] default-character-set utf8mb4 [mysqld] character-set-server utf8mb4 collation-server utf8mb4_unicode_ci重启 MySQL 服务。检查JdbcUtil.java中连接 URLString url jdbc:mysql://localhost:3306/bookstore?useUnicodetruecharacterEncodingutf8mb4serverTimezoneGMT%2B8;4.4 现象管理员后台添加图书时图片上传失败image字段存入数据库为null原因JSP 页面使用了input typefile但 Servlet 未用Apache Commons FileUpload解析 multipart 请求而是直接request.getParameter(image)—— 这永远返回null。解决该项目实际未实现图片上传功能image字段是手动插入 SQL 的字符串路径如images/java-web.jpg前端img src${book.image}直接引用。若你误以为支持上传请勿修改addBook.jsp的 form enctype否则request.getParameter()会失效。正确做法将图片放入web/images/目录SQL 中写相对路径。4.5 现象下单后库存未减少或订单明细未生成原因OrderService.placeOrder()中的事务未生效常见于conn.setAutoCommit(false)后忘记conn.commit()或catch块中rollback()未执行因conn为 null。解决在placeOrder()方法开头加日志System.out.println(Starting transaction...);在conn.commit()后加日志System.out.println(Transaction committed.);在catch块中加System.out.println(Transaction rolled back.);若只看到 start 日志看不到 commit 日志说明执行到某处抛异常检查bookDao.updateStock()的 SQL 是否语法错误如字段名写错。5. 功能验证与高分技巧三个必测场景 一个让老师眼前一亮的改造5.1 三类核心场景验证清单动手前先过一遍场景操作步骤预期结果验证点用户购物流程1. 访问/bookstore/login.jsp用admin/123登录2. 浏览图书点击“加入购物车”3. 点击“去结算”填写收货信息提交1. 登录后 header 显示“欢迎 admin”2. 购物车右上角数字13. 订单生成后orders表新增记录order_items表新增明细books表对应图书stock减1检查数据库bookstore的三张表数据是否同步变更管理员后台管理1. 访问/bookstore/admin/login.jsp用admin/123登录2. 点击“图书管理” → “添加图书”填 ISBN/书名/价格/库存3. 点击“用户管理”查看用户列表1. 添加后列表立即显示新书2.books表新增一行image字段为手动填的路径如images/spring-boot.jpg3.users表数据可读无敏感信息泄露确认admin/下所有 Servlet 的web.xml映射正确且AdminFilter若有拦截有效异常流程防御1. 在商品详情页手动修改 URL 中id123为id999999不存在的 ID2. 尝试用错误密码登录3. 库存为 0 的图书点击“加入购物车”1. 显示“商品不存在”提示页bookDetail.jsp有空指针判断2. 登录页显示红色错误提示“用户名或密码错误”3. 加入购物车按钮置灰或提示“库存不足”检查BookServlet、LoginServlet中的if (book null)和if (user null)判断是否健壮5.2 让老师刮目相看的改造给订单增加“支付状态”与“取消订单”功能原项目订单状态只有“待支付”缺乏电商核心状态流转。只需 30 分钟就能补全这个高分点Step 1扩展orders表ALTER TABLE orders ADD COLUMN pay_status ENUM(unpaid, paid, cancelled) DEFAULT unpaid, ADD COLUMN cancel_reason VARCHAR(255) NULL;Step 2新增PayServlet处理支付// src/com/bookstore/servlet/PayServlet.java public class PayServlet extends HttpServlet { private OrderService orderService new OrderService(); Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String orderId request.getParameter(orderId); // 模拟支付成功实际应对接支付网关 if (orderService.payOrder(orderId)) { request.setAttribute(msg, 支付成功); } else { request.setAttribute(msg, 支付失败请重试); } request.getRequestDispatcher(/orderDetail.jsp?orderId orderId).forward(request, response); } }Step 3在orderDetail.jsp添加按钮c:if test${order.payStatus unpaid} form action${pageContext.request.contextPath}/pay methodpost input typehidden nameorderId value${order.orderId} button typesubmit立即支付/button /form a href${pageContext.request.contextPath}/cancelOrder?orderId${order.orderId}取消订单/a /c:ifStep 4实现CancelOrderServlet关键恢复库存// src/com/bookstore/servlet/CancelOrderServlet.java public class CancelOrderServlet extends HttpServlet { private OrderService orderService new OrderService(); Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String orderId request.getParameter(orderId); if (orderService.cancelOrder(orderId)) { request.setAttribute(msg, 订单已取消库存已恢复); } request.getRequestDispatcher(/orderList.jsp).forward(request, response); } }为什么这个改造加分它体现了你对业务状态机的理解unpaid → paid → cancelled、对数据一致性的把控取消订单必须回滚库存、以及对MVC 分层的熟练运用状态变更逻辑在 ServiceUI 控制在 JSP路由在 Servlet。老师批注时会写“状态设计合理事务处理严谨”。6. 从那以后我每次交 JavaWeb 大作业都强制走一遍这三步验证第一启动前必查web.xml和JdbcUtil.javaweb.xml里所有servlet-mapping的url-pattern是否与 JSP 表单的action匹配比如actionlogin对应url-pattern/login/url-patternJdbcUtil.java的url、username、password是否与你本地 MySQL 一致characterEncoding参数是否带上。有一次我因为复制粘贴漏了serverTimezoneGMT%2B8时间戳全错成 1970 年debug 两小时才发现。第二功能测试必过“脏数据”关不只测正常流程一定手动构造异常输入——在登录框输 OR 11测试 SQL 注入防护本项目未做预编译所以会失败但你要知道这是个漏洞在 URL 里把id1改成id-1看是否报 500应该友好提示上传一个超大图片文件100MB看是否内存溢出本项目无上传但你要意识到边界。老师最看重的不是功能多炫而是你有没有工程思维。第三答辩前必导出一份“干净版”删掉所有System.out.println()日志注释掉调试用的Thread.sleep(5000)把README.md里的部署步骤重写成“三步法”1. 配 JDK8 2. 执行 SQL 3. IDEA 导入再截图首页、后台、订单页各一张。答辩时老师问“你最大的收获”我就说“以前觉得 MVC 是三个文件夹现在明白它是责任契约——Servlet 不碰 SQLService 不管跳转DAO 不管事务。这份代码让我第一次写出能被别人接手的 JavaWeb。”希望帮到你。本文还有配套的精品资源点击获取
返回列表