Java Web点餐系统:Servlet+Bootstrap+MySQL实战解析 📅 发布时间:2026/9/11 23:08:26 👁 浏览次数: 简介本资源是一套完整的Java课程设计项目——基于BootstrapJSPServletMySQL实现的咖啡厅点餐系统源码面向计算机相关专业在校学生、教师及初入行业的开发人员适用于课程设计、毕设选题、Web开发入门实践与二次功能拓展。压缩包共390个文件涵盖107个Java源码、109个编译后Class文件、26个JSP页面、78张JPG与28张PNG界面截图、13个JS交互脚本、12个Jar依赖库及1个完整SQL建库脚本总大小11.93MB结构清晰模块划分明确含用户管理、商品展示、购物车、订单处理、登录鉴权等核心功能。已有157人学习下载所有代码均经实测可正常运行配套项目说明文档详述部署步骤与数据库配置便于快速上手内容预览可见LoginServlet、OrderServiceImpl、ItemDaoImpl等典型分层实现类体现MVC架构思想与JDBC操作规范是理解传统Java Web开发全流程的优质教学范例。1. 这不是又一个“Hello World”JSP项目它用原生ServletBootstrap跑通了真实点餐闭环你可能刚在IDEA里配好Tomcat却卡在web.xml路径映射报404也可能改完JSP页面刷新十次还是旧样式怀疑浏览器缓存、服务器热部署、甚至自己手速——而这个咖啡厅点餐系统恰恰踩中了Java Web初学者最痛的三个锚点Servlet请求链路不透明、Bootstrap组件与JSP数据绑定脱节、MySQL事务在订单场景下失效。它没用Spring Boot自动装配所有DAO层手动写JDBC连接池、所有页面跳转靠RequestDispatcher.forward()硬编码、所有表单校验在LoginServlet里用正则硬判断。这意味着你能一眼看清OrderServiceImpl里conn.setAutoCommit(false)之后哪一行抛异常导致回滚失败能顺着ShoppingcartItemDaoImpl的executeUpdate()调用定位到SQL拼接时漏掉的单引号更能从AutoLoginFilter的chain.doFilter()前后日志理解过滤器链如何劫持未登录用户的/order/list请求。它适合两类人一类是正在被《Java Web程序设计》课设 deadline 追着跑的大二学生另一类是想撕开Spring Boot封装、重拾Servlet容器底层逻辑的三年经验开发者。2. 从.class反编译到可运行工程还原JSPServlet项目结构的关键四步2.1 解压后第一眼必须确认的三个文件夹层级关系拿到Java课设基于BootStrapJSPServletMysql的咖啡厅点餐系统源码含项目说明数据库sql.zip后解压目录结构决定你能否顺利导入IDE。典型错误是直接把src文件夹拖进IDEA——这会导致web.xml找不到、JSP编译路径错乱。正确路径必须是coffee-shop/ ├── src/ # Java源码含servlet、dao、service ├── WebContent/ # 标准WebRoot含WEB-INF/web.xml、jsp、css、js │ ├── WEB-INF/ │ │ ├── web.xml # 核心配置servlet-mapping和filter定义 │ │ └── lib/ # mysql-connector-java-5.1.47.jar必须在此 │ ├── index.jsp │ └── static/ # Bootstrap CSS/JS实际存放位置非CDN引用 ├── db/ # 包含coffee_shop.sql建库脚本 └── README.md # 项目说明文档含数据库用户名密码提示若解压后只有.class文件如LoginServlet.class说明作者上传的是编译后字节码。此时需用JD-GUI反编译获取源码重点检查web.xml中servlet-class是否指向正确包路径如com.coffee.servlet.LoginServlet否则Tomcat启动时会报ClassNotFoundException。2.2web.xml配置解析为什么GetHistoryOrdersPageServlet必须声明load-on-startup1/load-on-startup该系统的web.xml采用传统Servlet 3.0规范关键配置段如下servlet servlet-nameLoginServlet/servlet-name servlet-classcom.coffee.servlet.LoginServlet/servlet-class /servlet servlet-mapping servlet-nameLoginServlet/servlet-name url-pattern/login/url-pattern /servlet-mapping filter filter-nameAutoLoginFilter/filter-name filter-classcom.coffee.filter.AutoLoginFilter/filter-class /filter filter-mapping filter-nameAutoLoginFilter/filter-name url-pattern/order/*/url-pattern /filter-mapping这里有两个易错点URL模式匹配陷阱url-pattern/order/*/url-pattern仅拦截/order/list、/order/submit等路径但不会拦截/order.jsp因JSP是静态资源由DefaultServlet处理。若用户直接访问http://localhost:8080/coffee/order.jsp过滤器失效需在JSP顶部强制校验session.getAttribute(user) ! null。Filter执行顺序AutoLoginFilter在web.xml中声明顺序靠前确保在LoginServlet之前拦截请求。若将其放在servlet之后可能导致未登录用户绕过过滤器直接访问订单页。2.2.1 验证web.xml生效的三行命令启动Tomcat后用curl验证配置是否生效# 检查LoginServlet映射应返回302重定向到index.jsp curl -I http://localhost:8080/coffee/login # 检查Filter拦截效果未登录时访问/order/list应返回302跳转login.jsp curl -I http://localhost:8080/coffee/order/list # 检查静态资源路径Bootstrap CSS应返回200 curl -I http://localhost:8080/coffee/static/css/bootstrap.min.css注意若curl -I返回404先检查Tomcat控制台日志中是否有SEVERE: Servlet.service() for servlet [LoginServlet] threw exception这通常意味着com.coffee.servlet.LoginServlet类路径错误或缺少mysql-connector-java.jar。2.3 数据库SQL脚本执行要点字符集与外键约束必须手动开启db/coffee_shop.sql脚本包含建库、建表、插入初始数据三部分。执行时常见失败原因及修复方案问题现象根本原因解决命令ERROR 1064 (42000): You have an error in your SQL syntaxMySQL 8.0默认禁用CREATE TABLE ... ENGINEInnoDB DEFAULT CHARSETutf8中的utf8实际应为utf8mb4SET NAMES utf8mb4;执行后再导入ERROR 1215 (HY000): Cannot add foreign key constraintshoppingcart_item表中order_id字段类型与orders.id不一致如前者INT后者BIGINT修改shoppingcart_item.order_id为BIGINT UNSIGNEDjava.sql.SQLException: Field password doesnt have a default valueusers表password字段设为NOT NULL但INSERT语句未提供值在INSERT前执行SET sql_modeSTRICT_TRANS_TABLES;关闭严格模式执行步骤以MySQL 8.0为例-- 步骤1创建数据库并指定字符集 CREATE DATABASE coffee_shop CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- 步骤2切换数据库 USE coffee_shop; -- 步骤3执行建表SQL注意替换原脚本中的utf8为utf8mb4 -- 步骤4插入测试数据管理员账号admin/admin123 INSERT INTO users(username, password, email, role) VALUES (admin, e10adc3949ba59abbe56e057f20f883e, admincoffee.com, ADMIN);提示密码admin123的MD5值e10adc3949ba59abbe56e057f20f883e已硬编码在LoginServlet.java中若需修改密码必须同步更新SQL插入语句和Servlet里的校验逻辑。2.4 JSP与Bootstrap协同渲染解决“下拉菜单点击无反应”的CSS加载顺序问题系统前端使用Bootstrap 3.3.7非CDN本地文件存于WebContent/static/但JSP页面中常出现下拉菜单、模态框失效。根本原因是Bootstrap JS依赖jQuery而index.jsp中JS加载顺序错误!-- 错误写法Bootstrap JS在jQuery之前 -- script srcstatic/js/bootstrap.min.js/script script srcstatic/js/jquery.min.js/script !-- 正确写法jQuery必须在Bootstrap JS之前 -- script srcstatic/js/jquery.min.js/script script srcstatic/js/bootstrap.min.js/script此外nav标签中下拉菜单HTML结构必须符合Bootstrap 3规范!-- 正确结构 -- li classdropdown a href# classdropdown-toggle>script $(document).ready(function(){ console.log(jQuery loaded, Bootstrap dropdown should work); }); /script3. 订单提交全流程代码追踪从JSP表单到MySQL事务提交的七层调用链3.1 表单提交路径cart.jsp→OrderServlet→OrderServiceImpl→OrderDaoImpl用户在cart.jsp点击“提交订单”按钮触发表单提交!-- cart.jsp 中的关键form -- form actionorder/submit methodpost input typehidden nameuserId value${sessionScope.user.id} c:forEach items${cartItems} varitem input typehidden nameitemId value${item.id} input typehidden namequantity value${item.quantity} /c:forEach button typesubmit classbtn btn-primary提交订单/button /form该表单提交至/order/submit路径由web.xml中定义的OrderServlet处理servlet servlet-nameOrderServlet/servlet-name servlet-classcom.coffee.servlet.OrderServlet/servlet-class /servlet servlet-mapping servlet-nameOrderServlet/servlet-name url-pattern/order/submit/url-pattern /servlet-mapping3.1.1OrderServlet.java核心逻辑参数解析与服务层调用protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 1. 获取用户ID从session读取非表单传参防篡改 User user (User) request.getSession().getAttribute(user); if (user null) { response.sendRedirect(request.getContextPath() /login.jsp); return; } // 2. 解析购物车项注意request.getParameterValues()返回String[] String[] itemIds request.getParameterValues(itemId); String[] quantities request.getParameterValues(quantity); // 3. 构建Order对象并调用Service Order order new Order(); order.setUserId(user.getId()); order.setStatus(PENDING); OrderService orderService new OrderServiceImpl(); try { orderService.createOrder(order, itemIds, quantities); request.setAttribute(message, 订单提交成功); request.getRequestDispatcher(/order/success.jsp).forward(request, response); } catch (Exception e) { request.setAttribute(error, 订单创建失败 e.getMessage()); request.getRequestDispatcher(/order/cart.jsp).forward(request, response); } }逻辑说明OrderServlet不直接操作数据库而是将itemIds和quantities数组传递给OrderServiceImpl。此处request.getParameterValues(itemId)返回字符串数组需在Service层转换为Long类型否则OrderDaoImpl执行SQL时会因类型不匹配报错。3.2 Service层事务控制OrderServiceImpl的createOrder方法拆解OrderServiceImpl.createOrder()方法实现订单创建的原子性操作关键代码如下public void createOrder(Order order, String[] itemIds, String[] quantities) throws SQLException { Connection conn null; PreparedStatement ps null; try { conn JdbcUtil.getConnection(); // 从自定义连接池获取连接 conn.setAutoCommit(false); // 关闭自动提交开启事务 // 步骤1插入orders表获取生成的order_id String insertOrderSql INSERT INTO orders(user_id, status, create_time) VALUES(?, ?, NOW()); ps conn.prepareStatement(insertOrderSql, Statement.RETURN_GENERATED_KEYS); ps.setLong(1, order.getUserId()); ps.setString(2, order.getStatus()); ps.executeUpdate(); ResultSet rs ps.getGeneratedKeys(); long orderId 0; if (rs.next()) { orderId rs.getLong(1); } // 步骤2批量插入shoppingcart_item关联记录 String insertItemSql INSERT INTO shoppingcart_item(order_id, item_id, quantity) VALUES(?, ?, ?); ps conn.prepareStatement(insertItemSql); for (int i 0; i itemIds.length; i) { ps.setLong(1, orderId); ps.setLong(2, Long.parseLong(itemIds[i])); // 关键字符串转long ps.setInt(3, Integer.parseInt(quantities[i])); ps.addBatch(); } ps.executeBatch(); // 步骤3清空用户购物车删除shoppingcart_item中对应user_id的记录 String clearCartSql DELETE FROM shoppingcart_item WHERE user_id ?; ps conn.prepareStatement(clearCartSql); ps.setLong(1, order.getUserId()); ps.executeUpdate(); conn.commit(); // 所有操作成功提交事务 } catch (SQLException e) { if (conn ! null) { conn.rollback(); // 任一环节失败回滚整个事务 } throw e; } finally { JdbcUtil.close(null, ps, conn); // 确保连接释放 } }参数说明Statement.RETURN_GENERATED_KEYS参数使ps.getGeneratedKeys()能获取orders.id自增主键值ps.addBatch()和ps.executeBatch()实现批量插入避免N1查询JdbcUtil.close()是自定义工具类确保Connection在finally块中关闭防止连接泄漏。3.3 DAO层SQL执行OrderDaoImpl中预编译语句的占位符安全实践OrderDaoImpl作为数据访问对象其insertOrder方法体现JDBC最佳实践public void insertOrder(Order order) throws SQLException { String sql INSERT INTO orders(user_id, status, create_time) VALUES(?, ?, ?); PreparedStatement ps conn.prepareStatement(sql); ps.setLong(1, order.getUserId()); // 使用setLong()而非setString()类型强校验 ps.setString(2, order.getStatus()); // status字段为VARCHAR用setString() ps.setTimestamp(3, new Timestamp(System.currentTimeMillis())); // 时间戳用setTimestamp() ps.executeUpdate(); }对比易错写法// 危险字符串拼接SQL导致SQL注入 String sql INSERT INTO orders(user_id, status) VALUES( order.getUserId() , order.getStatus() );提示ps.setLong(1, order.getUserId())中第一个参数1表示SQL中第一个?占位符的位置若顺序错位如ps.setLong(2, order.getUserId())会导致SQLException: Parameter index out of range。4. 排查高频故障从404到500的五类错误定位与修复指南4.1 404错误路径映射与部署路径的双重校验当访问http://localhost:8080/coffee/login返回404按以下顺序排查检查项命令/操作预期结果修复方案Tomcat是否部署成功查看tomcat/logs/catalina.out末尾应有INFO: Server startup in [xxx] ms若无此日志检查server.xml中Host配置应用上下文路径是否正确访问http://localhost:8080/查看Tomcat管理界面coffee应用状态应为running若显示stopped在管理界面点击Startweb.xml中servlet-mapping是否匹配检查url-pattern/login/url-pattern与请求URL/login必须与浏览器地址栏完全一致区分大小写若项目部署路径为/coffee则URL应为/coffee/login类文件是否在正确位置查看WEB-INF/classes/com/coffee/servlet/LoginServlet.class文件存在且包路径正确若只有.java源码需在IDEA中Build - Build Project生成class4.1.1 快速验证Servlet是否注册成功在LoginServlet.java的doPost方法开头添加日志protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println( LoginServlet received POST request); // 控制台输出 // ...原有逻辑 }重启Tomcat后若控制台无此输出证明Servlet未被容器加载90%概率是web.xml中servlet-class路径错误。4.2 500错误空指针与数据库连接的连锁反应java.lang.NullPointerException在OrderServlet中高频出现根源通常是request.getSession().getAttribute(user)返回null。修复流程确认登录流程是否完整LoginServlet中request.getSession().setAttribute(user, user)必须执行成功检查Session超时设置web.xml中添加session-config session-timeout30/session-timeout !-- 单位分钟 -- /session-config验证数据库连接在JdbcUtil.getConnection()中添加日志public static Connection getConnection() throws SQLException { System.out.println( Attempting to get DB connection...); Connection conn dataSource.getConnection(); System.out.println( DB connection successful: conn); return conn; }4.3 JSP页面中文乱码request与response编码的双端设置login.jsp中输入中文用户名提交后LoginServlet中request.getParameter(username)返回乱码。解决方案4.3.1 请求端编码统一JSP页面% page languagejava contentTypetext/html; charsetUTF-8 pageEncodingUTF-8% !DOCTYPE html html head meta charsetUTF-8 !-- 页面meta声明 -- title登录/title /head body form actionlogin methodpost accept-charsetUTF-8 !-- 表单显式声明编码 --4.3.2 服务端编码强制设置Servletprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding(UTF-8); // 设置请求编码 response.setContentType(text/html;charsetUTF-8); // 设置响应编码 String username request.getParameter(username); // 此时username为正确中文 // ...后续逻辑 }4.4 Bootstrap组件不生效静态资源路径与MIME类型的隐性冲突index.jsp中link relstylesheet hrefstatic/css/bootstrap.min.css返回404但文件物理存在。原因可能是Tomcat未正确识别CSS MIME类型。修复方法在web.xml中添加静态资源处理配置servlet-mapping servlet-namedefault/servlet-name url-pattern*.css/url-pattern /servlet-mapping servlet-mapping servlet-namedefault/servlet-name url-pattern*.js/url-pattern /servlet-mapping注意servlet-namedefault/servlet-name是Tomcat内置的DefaultServlet专门处理静态资源。若缺失此配置Tomcat会尝试用JspServlet处理CSS文件导致404。4.5 MySQL连接拒绝驱动版本与URL参数的兼容性陷阱java.sql.SQLException: Access denied for user rootlocalhost错误即使用户名密码正确。原因常是MySQL 8.0默认认证插件变更。解决方案修改MySQL用户认证方式ALTER USER rootlocalhost IDENTIFIED WITH mysql_native_password BY your_password; FLUSH PRIVILEGES;更新JDBC URL参数db.properties或硬编码jdbc.urljdbc:mysql://localhost:3306/coffee_shop?useSSLfalseserverTimezoneGMT%2B8allowPublicKeyRetrievaltrue关键参数说明useSSLfalse禁用SSL开发环境常用serverTimezoneGMT%2B8解决时区错误allowPublicKeyRetrievaltrue适配MySQL 8.0公钥检索机制。5. 二次开发实战为咖啡厅系统增加“订单状态实时更新”功能5.1 前端轮询机制用JavaScript定时请求订单状态在order/list.jsp中添加状态轮询逻辑避免手动刷新script function checkOrderStatus() { fetch(%request.getContextPath()%/order/status?orderId orderId) .then(response response.json()) .then(data { if (data.status SHIPPED) { document.getElementById(status- orderId).innerText 已发货; document.getElementById(status- orderId).className label label-success; clearInterval(pollingInterval); // 状态变更后停止轮询 } }) .catch(error console.error(轮询失败:, error)); } // 每5秒轮询一次 const pollingInterval setInterval(checkOrderStatus, 5000); /script5.2 后端状态查询接口OrderStatusServlet的轻量实现新建OrderStatusServlet.java仅查询单个订单状态WebServlet(/order/status) public class OrderStatusServlet extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String orderId request.getParameter(orderId); if (orderId null || orderId.trim().isEmpty()) { response.sendError(HttpServletResponse.SC_BAD_REQUEST, Missing orderId); return; } OrderDao orderDao new OrderDaoImpl(); try { Order order orderDao.findById(Long.parseLong(orderId)); response.setContentType(application/json;charsetUTF-8); response.getWriter().write({\status\:\ order.getStatus() \}); } catch (Exception e) { response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage()); } } }技巧使用WebServlet注解替代web.xml配置减少XML冗余response.getWriter().write()直接输出JSON字符串比引入Jackson更轻量。5.3 数据库触发器自动更新订单状态时间戳为orders表添加update_time字段并创建触发器ALTER TABLE orders ADD COLUMN update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP; DELIMITER $$ CREATE TRIGGER update_order_time BEFORE UPDATE ON orders FOR EACH ROW BEGIN SET NEW.update_time NOW(); END$$ DELIMITER ;此触发器确保每次UPDATE orders SET statusSHIPPED WHERE id123时update_time自动更新前端可通过比较update_time与当前时间判断状态是否最新。最后一行技术内容在OrderDaoImpl.updateStatus()方法中执行UPDATE orders SET status?, update_timeNOW() WHERE id?时update_timeNOW()可省略因触发器已自动维护。本文还有配套的精品资源点击获取