源碼到運行避坑:JavaWeb超市訂單管理系統(tǒng)的JSP+Servlet實戰(zhàn))
簡介基于Javaweb的超市訂單管理系統(tǒng)項目源碼是一套達到95分以上課程設(shè)計水平的高分參考實現(xiàn)代碼完整適合高校Java Web課設(shè)、畢業(yè)設(shè)計或自學(xué)練習(xí)。系統(tǒng)圍繞超市訂單管理核心業(yè)務(wù)展開覆蓋訂單處理、基礎(chǔ)數(shù)據(jù)維護、權(quán)限控制等常見功能場景采用基于JSPServletJDBC的經(jīng)典分層設(shè)計前端頁面與后端控制邏輯清晰便于理解完整的Java Web請求處理流程。資源壓縮包共包含135個文件核心文件類型有24個Java源文件、24個JSP頁面、25個JavaScript腳本、9個CSS樣式和10個JAR依賴包另有3個SQL數(shù)據(jù)庫腳本、界面截圖、字體圖標(biāo)等輔助內(nèi)容整體僅2.69MB小巧完整。項目中SQL腳本可快速初始化數(shù)據(jù)庫表格和測試數(shù)據(jù)頁面樣式整合了layui等常用組件界面簡潔統(tǒng)一后端Servlet和DAO層代碼規(guī)范適合作為模板進行功能擴展。目前已有281人學(xué)習(xí)瀏覽適合需要快速落地課設(shè)并深入學(xué)習(xí)訂單流水的Java學(xué)習(xí)者。1. 拿到一份“超市訂單管理系統(tǒng)”課設(shè)源碼第一步不是跑起來而是搞懂它每年畢設(shè)和課設(shè)季都會有一批人下載“基于Javaweb的超市訂單管理系統(tǒng)源碼數(shù)據(jù)庫”這類壓縮包。解壓后里面通常是整套 JSP 頁面、Servlet 類、DAO 層和一個 init.sql運氣好還帶一份寫好的課設(shè)報告。問題在于這種項目壓縮包如果只是原樣導(dǎo)入 IDEA 再點運行八成會卡在環(huán)境配置上即使跑起來了答辯時被問“訂單狀態(tài)是怎么流轉(zhuǎn)的”也可能答不上來。這篇就按我做課設(shè)指導(dǎo)最常見的路徑來講先拆技術(shù)棧和目錄再理數(shù)據(jù)庫設(shè)計然后把 JDBC 到 JSP 的請求鏈路走一遍最后把運行時的坑一次性避開。文章覆蓋的落點不是“看一遍就懂”而是“照著做就能復(fù)現(xiàn)”適合 JavaWeb 課程設(shè)計、期末項目以及第一次接觸完整 Web 項目的初學(xué)者。2. 項目結(jié)構(gòu)和技術(shù)選型為什么課設(shè)用 JSPServlet 最穩(wěn)2.1 為什么 JSPServletMySQL 是課設(shè)最不容易翻車的組合市面上 Java 后端方案很多Spring Boot 現(xiàn)在也進了不少高職和本科課堂但超市訂單管理系統(tǒng)這種課設(shè)選題我仍然建議優(yōu)先用 JSP Servlet MySQL。理由很現(xiàn)實課設(shè)的核心是“你能講清楚”不是“用了多新的框架”。Spring Boot 自動配置太多學(xué)生把項目跑起來后連請求怎么進 Controller 的鏈路都說不清答辯被追問兩句就容易露餡。JSP Servlet 的好處是請求鏈路短瀏覽器發(fā)請求Tomcat 找到對應(yīng)的 ServletServlet 調(diào) DAO 查 MySQL再把結(jié)果塞進 request 域轉(zhuǎn)發(fā)給 JSPJSP 渲染完返回到瀏覽器。整條鏈路每一環(huán)都能在代碼里找到實體老師問“這個頁面數(shù)據(jù)從哪來的”都是可以指著代碼講的。而且課程大綱里往往明確寫了 Servlet、JSP、JDBC 這幾個知識點用這個組合屬于“踩在大綱上做項目”評分不會低。另外一個容易被忽略的點是項目體積。一套完整的超市訂單管理系統(tǒng)用 JSPServlet 寫大概 20 到 30 個 Java 文件加上 JSP 頁面結(jié)構(gòu)很清楚。換成 Spring Boot 就要多包一層 starter 依賴和自動配置課設(shè)報告里“系統(tǒng)架構(gòu)”那部分反而不好寫。還有一點關(guān)于依賴管理很多下載到的課設(shè)源碼不帶你熟悉的 Maven 工程結(jié)構(gòu)而是直接把一堆 jar 放在 WebContent/WEB-INF/lib 下。別急著嫌棄這種“扁平式”依賴對課設(shè)反而是優(yōu)勢——不依賴外網(wǎng)倉庫拷到哪都能編譯。2.2 IDEA 運行 Javaweb 項目配置Tomcat、JDK 版本與啟動順序拿到源碼后的第一個動作別急著點 Run先把三個東西確認好JDK 版本、Tomcat 版本、Artifacts 配置。我一般按這個順序做先看java -versionJDK 8 和 JDK 11 都能跑這類項目但注意 Tomcat 版本要匹配。Tomcat 8.5 對應(yīng) JDK 8 沒問題Tomcat 9 也能用 JDK 8但如果你系統(tǒng)里是 JDK 17建議裝 Tomcat 9.0.xTomcat 7 在 JDK 17 上會因為模塊訪問問題直接啟動失敗。IDEA 里運行 JavaWeb 項目的配置路徑是Run - Edit Configurations - 左上角加號 - Tomcat Server - Local。注意不是加 “Application” 那種普通配置。選好本地 Tomcat 路徑后切到 Deployment 頁簽把 Artifact 添加進去Application context 統(tǒng)一寫成/supermarket這樣訪問路徑就是http://localhost:8080/supermarket。然后是 Artifacts。打開 Project Structure - Artifacts確認有沒有一個 Exploded 類型的 war 包。沒有就手動加點加號 - Web Application: Exploded - From Modules把模塊關(guān)聯(lián)上。這里常見的錯誤是頁面能打開但 CSS 和圖片 404多半是 Artifacts 里沒有把 Web 資源目錄標(biāo)進去。正常結(jié)構(gòu)應(yīng)該是Artifact 的根目錄下有WEB-INF、jsp、static這些文件夾而不是把src或sql目錄也打進去。第三個容易忽略的是web.xml所在位置。Servlet 3.0 以上支持注解也支持 web.xml但如果你用的是注解方式而 web.xml 版本寫得低于 2.5Tomcat 可能會按 2.3 的老模式解析導(dǎo)致WebServlet不生效。這個坑我放在第 5 章專門說但配置環(huán)境時先確認WebContent/WEB-INF/web.xml存在且文件頭聲明的是 3.0 或更高版本。2.3 拿到源碼后的目錄檢查清單src、WebContent、lib 和 sql 缺一不可不管源碼是從什么渠道來的先按下面的結(jié)構(gòu)核對一遍。一個“95 分以上”的課設(shè)項目目錄上至少該有這四塊supermarket/ ├── sql/ # 數(shù)據(jù)庫腳本通常叫 init.sql 或 supermarket.sql │ └── init.sql ├── src/ # Java 源碼按 com.xxx.util / dao / servlet 分包 │ ├── com/supermarket/util/ │ ├── com/supermarket/dao/ │ ├── com/supermarket/servlet/ │ └── com/supermarket/model/ └── WebContent/ # Web 根目錄 ├── META-INF/ ├── WEB-INF/ │ ├── lib/ # 依賴 jar 包 │ └── web.xml ├── jsp/ # 頁面文件 └── index.jsp如果下載的包解壓后只有源碼沒有 sql 腳本或者只有 sql 沒有 Java 源碼那后面所有步驟都走不通。驗證目錄最簡單的方式是用命令行tree -L 2 -d supermarket/沒有 tree 命令就在資源管理器里看。檢查優(yōu)先級最高的是WEB-INF/lib里有沒有 mysql 驅(qū)動 jar驅(qū)動都缺的話項目跑起來后第一個報錯就是ClassNotFoundException: com.mysql.cj.jdbc.Driver。這個結(jié)構(gòu)同時也是一個“自查清單”如果某個下載包把 Java 源碼塞在別的位置或者 JSP 頁面全部放在根目錄而沒有 jsp 子目錄建議重新整理一下。別嫌麻煩整理目錄的過程其實就是熟悉項目的過程答辯時被問到“項目結(jié)構(gòu)是怎樣的”你能直接畫出這個圖加分效果比多寫十行代碼都強。3. 數(shù)據(jù)庫六張核心表從建庫 SQL 到訂單流轉(zhuǎn)的增刪改查3.1 主數(shù)據(jù)表管理員、供應(yīng)商、商品、客戶超市訂單管理系統(tǒng)說到底就是圍繞“誰在賣、賣什么、賣給誰、誰來審批”四個問題轉(zhuǎn)。大多數(shù)課設(shè)源碼的數(shù)據(jù)庫都不會少于四張主數(shù)據(jù)表admin_info管理員表負責(zé)登錄后臺、審核訂單supplier_info供應(yīng)商表記錄商品從哪進貨goods_info商品表商品名稱、分類、進價、售價、庫存customer_info客戶表收貨人、聯(lián)系電話、配送地址。建表 SQL 是整套系統(tǒng)的基礎(chǔ)也是課設(shè)報告里“數(shù)據(jù)庫設(shè)計”章節(jié)直接用得上的內(nèi)容。我用 MySQL 8 為例注意字符集要明確寫成 utf8mb4避免中文亂碼CREATE DATABASE IF NOT EXISTS supermarket DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_general_ci; USE supermarket; CREATE TABLE admin_info ( admin_id INT PRIMARY KEY AUTO_INCREMENT COMMENT 管理員編號, login_name VARCHAR(50) NOT NULL UNIQUE COMMENT 登錄賬號, password VARCHAR(64) NOT NULL COMMENT 密碼, real_name VARCHAR(50) COMMENT 真實姓名, role VARCHAR(20) DEFAULT ADMIN COMMENT 角色 ) ENGINEInnoDB COMMENT管理員表; CREATE TABLE supplier_info ( supplier_id INT PRIMARY KEY AUTO_INCREMENT COMMENT 供應(yīng)商編號, supplier_name VARCHAR(100) NOT NULL COMMENT 供應(yīng)商名稱, contact VARCHAR(50) COMMENT 聯(lián)系人, phone VARCHAR(20) COMMENT 聯(lián)系電話, address VARCHAR(200) COMMENT 地址, status TINYINT DEFAULT 1 COMMENT 1啟用 0停用 ) ENGINEInnoDB COMMENT供應(yīng)商表; CREATE TABLE goods_info ( goods_id INT PRIMARY KEY AUTO_INCREMENT COMMENT 商品編號, goods_name VARCHAR(100) NOT NULL COMMENT 商品名稱, category VARCHAR(50) COMMENT 分類如飲料/日用品, supplier_id INT COMMENT 供應(yīng)商編號, price DECIMAL(10,2) COMMENT 售價, stock INT DEFAULT 0 COMMENT 庫存, status TINYINT DEFAULT 1 COMMENT 1上架 0下架 ) ENGINEInnoDB COMMENT商品表; CREATE TABLE customer_info ( customer_id INT PRIMARY KEY AUTO_INCREMENT COMMENT 客戶編號, customer_name VARCHAR(50) NOT NULL COMMENT 客戶名稱, phone VARCHAR(20) COMMENT 聯(lián)系電話, address VARCHAR(200) COMMENT 配送地址, create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 創(chuàng)建時間 ) ENGINEInnoDB COMMENT客戶表;登錄名和密碼字段建議用login_name而不是username因為username在部分 MySQL 版本里沒有保留字問題但在拼接 SQL 時容易和其他上下文混淆。密碼字段長度給 64 是為了兼容 MD5 或 SHA-256 加密后的長度如果源碼里用明文存儲至少留一個改造成加密的空間。3.2 訂單主表與明細表為什么要拆兩張表訂單模塊是這個系統(tǒng)的核心也是評分老師一定會看的地方。訂單主表存一次訂單的總體信息明細表存這個訂單買了哪些商品、各買多少、當(dāng)時成交價是多少。拆開的原因很直接一個訂單可能包含多件商品如果所有商品都塞在主表的一行里要么字段重復(fù)設(shè)計要么就得用逗號拼接商品 ID查詢和統(tǒng)計都很難做。主表與明細表的標(biāo)準(zhǔn) SQL 如下CREATE TABLE order_info ( order_id INT PRIMARY KEY AUTO_INCREMENT COMMENT 訂單編號, order_no VARCHAR(32) NOT NULL UNIQUE COMMENT 訂單號業(yè)務(wù)編號, customer_id INT NOT NULL COMMENT 客戶編號, total_amount DECIMAL(10,2) DEFAULT 0 COMMENT 訂單總金額, status TINYINT DEFAULT 0 COMMENT 0待審核 1已通過 2已出庫 3已完成 4已取消, remark VARCHAR(255) COMMENT 備注, create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 下單時間, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 更新時間 ) ENGINEInnoDB COMMENT訂單主表; CREATE TABLE order_item ( item_id INT PRIMARY KEY AUTO_INCREMENT COMMENT 明細編號, order_id INT NOT NULL COMMENT 訂單編號, goods_id INT NOT NULL COMMENT 商品編號, quantity INT NOT NULL DEFAULT 1 COMMENT 購買數(shù)量, price DECIMAL(10,2) COMMENT 成交單價, amount DECIMAL(10,2) COMMENT 小計金額 ) ENGINEInnoDB COMMENT訂單明細表;訂單號order_no和主鍵order_id分開是我特意推薦的做法。主鍵自增編號用來做表關(guān)聯(lián)訂單號對外展示可以用時間戳加隨機數(shù)生成比如20250615143000001。這樣有兩個好處對外看不出系統(tǒng)每天有多少訂單以后做訂單號查重也方便。回答老師“為什么有兩個編號”時也能作為設(shè)計亮點。明細表里的price字段存的是“成交單價”不是“商品當(dāng)前售價”。這是因為商品售價以后可能調(diào)而歷史訂單里的價格不應(yīng)該跟著變。這個細節(jié)在課設(shè)報告里寫一句“記錄下單時的快照價格”能看出你理解了一致性設(shè)計屬于典型的加分操作。3.3 寫一份可直接導(dǎo)入的 init.sql外鍵關(guān)系與演示數(shù)據(jù)一份合格的課設(shè)腳本要有外鍵、索引、演示數(shù)據(jù)和導(dǎo)入驗證命令。很多源碼的 SQL 腳本里只有 create 語句沒有 insert 數(shù)據(jù)導(dǎo)入后頁面上什么都查不到容易誤判是項目壞了。我建議自己補一份演示數(shù)據(jù)每種表至少插入 3 到 5 條記錄訂單至少要有“待審核”“已通過”“已完成”三種狀態(tài)各一條這樣前端每個狀態(tài)頁面都有數(shù)據(jù)可以展示。給訂單相關(guān)表補數(shù)據(jù)時注意先插主表拿到自增的order_id再插明細表??梢灾苯訉懰罃?shù)值腳本一次跑完示例INSERT INTO admin_info (login_name, password, real_name) VALUES (admin, admin123, 系統(tǒng)管理員); INSERT INTO supplier_info (supplier_name, contact, phone) VALUES (華南食品供應(yīng)商, 張經(jīng)理, 13800000001); INSERT INTO goods_info (goods_name, category, supplier_id, price, stock) VALUES (農(nóng)夫山泉550ml, 飲料, 1, 2.50, 200), (樂事薯片原味, 零食, 1, 6.90, 120); INSERT INTO customer_info (customer_name, phone, address) VALUES (李四, 13900000001, 幸福小區(qū)3棟101); INSERT INTO order_info (order_no, customer_id, total_amount, status) VALUES (20250610001, 1, 13.80, 1); INSERT INTO order_item (order_id, goods_id, quantity, price, amount) VALUES (1, 2, 2, 6.90, 13.80);導(dǎo)入命令用 MySQL 客戶端直接執(zhí)行mysql -u root -p init.sql導(dǎo)入后一定要驗證不要急著啟動 Tomcat。按順序執(zhí)行下面三條命令USE supermarket; SHOW TABLES; SELECT o.order_no, c.customer_name, o.total_amount, o.status FROM order_info o JOIN customer_info c ON o.customer_id c.customer_id;能查出數(shù)據(jù)說明建庫、外鍵、數(shù)據(jù)三個環(huán)節(jié)都正常。這一套驗證下來以后項目里頁面查不到訂單時你就能分清是數(shù)據(jù)庫的問題還是 Java 代碼的問題而不是兩眼一抹黑瞎調(diào)試。4. 請求鏈路怎么寫JDBC、Servlet 到 JSP 的完整代碼4.1 JDBC 連接參數(shù)中文亂碼與 MySQL 8 驅(qū)動是兩個必踩點先把數(shù)據(jù)庫連接工具類寫好這是所有 DAO 類都要依賴的入口。下面這個JdbcUtil是課設(shè)里能直接替換到項目里的標(biāo)準(zhǔn)寫法package com.supermarket.util; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; public class JdbcUtil { // 如果 MySQL 是 5.7把 com.mysql.cj.jdbc.Driver 換成 com.mysql.jdbc.Driver private static final String DRIVER com.mysql.cj.jdbc.Driver; private static final String URL jdbc:mysql://localhost:3306/supermarket ?useUnicodetruecharacterEncodingutf8 useSSLfalseserverTimezoneAsia/Shanghai; private static final String USER root; private static final String PASSWORD root; static { try { Class.forName(DRIVER); } catch (ClassNotFoundException e) { e.printStackTrace(); System.out.println(MySQL 驅(qū)動加載失敗請檢查 WEB-INF/lib 下是否有 mysql-connector jar 包); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(URL, USER, PASSWORD); } }這三個參數(shù)值得記一下useUnicodetruecharacterEncodingutf8少了任何一個頁面上輸入的中文存進數(shù)據(jù)庫就變成問號serverTimezoneAsia/Shanghai是 MySQL 8 的強制要求不寫會報時區(qū)錯誤useSSLfalse避免 MySQL 8 在本地連接時做 SSL 握手警告。密碼建議從單獨的配置讀取但課設(shè)階段直接寫在常量里問題不大。答辯被問“這樣寫安全嗎”時如實說“課設(shè)環(huán)境生產(chǎn)環(huán)境應(yīng)該把配置外置到 properties 文件”即可這反而是懂行的表現(xiàn)。4.2 用 BaseServlet 統(tǒng)一處理請求分發(fā)避免 Controller 里一堆 if同一個 Servlet 里往往要處理多個動作比如訂單模塊要有“查詢列表”“審核通過”“查詢明細”三個操作。新手寫法是在doGet里用 if 判斷參數(shù)三個操作寫出來還算能看等系統(tǒng)做到供貨商模塊、商品模塊時每個 Servlet 里都是嵌套 if維護起來很痛苦。更簡潔的方案是抽一個 BaseServlet利用反射把 action 參數(shù)映射成方法名package com.supermarket.servlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.lang.reflect.Method; public class BaseServlet extends HttpServlet { Override protected void service(HttpServletRequest req, HttpServletResponse resp) { String action req.getParameter(action); try { // 約定頁面?zhèn)鬟^來的 action 值和方法名保持一致 Method method this.getClass().getMethod(action, HttpServletRequest.class, HttpServletResponse.class); method.invoke(this, req, resp); } catch (Exception e) { e.printStackTrace(); throw new RuntimeException(找不到對應(yīng)的方法請檢查 action 參數(shù) action); } } }這段代碼要配合一個約定請求 URL 寫成orderServlet?actionaddOrder那么OrderServlet里就必須有一個addOrder(HttpServletRequest, HttpServletResponse)方法。方法名和 action 參數(shù)不一致時反射會直接報NoSuchMethodException這個異常信息比 if 分支的 404 好排查得多。4.3 訂單列表、審核和明細查詢?nèi)齻€核心接口的實現(xiàn)訂單模塊的 Servlet 繼承 BaseServlet然后按業(yè)務(wù)動作寫方法。下面這個例子只保留核心邏輯DAO 層的方法名與實現(xiàn)類保持一致package com.supermarket.servlet; import com.supermarket.dao.OrderDao; import com.supermarket.entity.OrderInfo; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.List; WebServlet(/orderServlet) public class OrderServlet extends BaseServlet { private OrderDao orderDao new OrderDao(); // 進入訂單管理頁 public void list(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String keyword req.getParameter(keyword); ListOrderInfo orders orderDao.findByCondition(keyword); req.setAttribute(orders, orders); req.getRequestDispatcher(/jsp/order/list.jsp).forward(req, resp); } // 訂單明細頁 public void detail(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { int orderId Integer.parseInt(req.getParameter(orderId)); req.setAttribute(order, orderDao.findById(orderId)); req.setAttribute(items, orderDao.findItems(orderId)); req.getRequestDispatcher(/jsp/order/detail.jsp).forward(req, resp); } // 審核通過把狀態(tài)從 0 改成 1 public void audit(HttpServletRequest req, HttpServletResponse resp) throws IOException { int orderId Integer.parseInt(req.getParameter(orderId)); orderDao.updateStatus(orderId, 1); resp.sendRedirect(orderServlet?actionlist); } }resp.sendRedirect和forward的區(qū)別是課設(shè)常見追問點。這里用 redirect 是因為審核完成后要重新查一次列表數(shù)據(jù)重新經(jīng)過一次完整的 Servlet 生命周期能保證列表刷新用 forward 的話地址欄不會變刷新頁面會重復(fù)提交表單?;卮稹盀槭裁床恢苯?forward 到 list.jsp”時能說出“重定向避免表單重復(fù)提交”說明你對 HTTP 狀態(tài)碼是有概念的。JSP 列表頁只做渲染不要寫 Java 業(yè)務(wù)代碼。數(shù)據(jù)已經(jīng)從 Servlet 放進了 request 域JSP 里用 JSTL 循環(huán)輸出即可% taglib prefixc urihttp://java.sun.com/jsp/jstl/core % table trth訂單號/thth客戶/thth金額/thth狀態(tài)/thth操作/th/tr c:forEach items${orders} varorder tr td${order.orderNo}/td td${order.customerName}/td td${order.totalAmount}/td td c:choose c:when test${order.status 0}待審核/c:when c:when test${order.status 1}已通過/c:when c:when test${order.status 2}已出庫/c:when c:otherwise已完成/c:otherwise /c:choose /td /tr /c:forEach /table狀態(tài)字段用數(shù)字存而不是直接存“待審核”三個字這是數(shù)據(jù)庫設(shè)計的基本功。頁面用c:choose把數(shù)字翻譯成文字以后想加“已退款”這種新狀態(tài)數(shù)據(jù)庫表不用改只改頁面和常量類就夠了。5. 課設(shè)運行避坑IDEA、Tomcat、MySQL 編碼的五個血淚現(xiàn)場5.1 現(xiàn)象項目一啟動 Tomcat 報錯控制臺看不到任何異常原因很多人下載源碼后直接右鍵 Java 文件選 Run而不是通過 Tomcat 啟動。JavaWeb 項目必須部署在 Web 容器里運行普通 main 方式是起不來的。還可能是因為 Tomcat 版本和項目編譯級別不匹配比如用 JDK17 跑 Tomcat7啟動日志會直接刷Unable to load class。解決先確認 Run/Debug Configurations 里配置的是 Tomcat Server不是 Application。然后到 File - Project Structure 檢查 Module 的語言級別Language Level和 JDK 版本。最后到控制臺看 Tomcat Localhost Log 標(biāo)簽頁這里有完整的部署日志。如果啟動還是失敗把 Tomcat 換到 8.5 或 9.0不要在一個版本上死磕。5.2 現(xiàn)象頁面上中文正常但數(shù)據(jù)庫里顯示“??”原因鏈接數(shù)據(jù)庫時沒有指定字符集編碼或者表結(jié)構(gòu)字符集不是 utf8。JDBC URL 里少了characterEncodingutf8是最常見情況其次是建庫語句沒寫DEFAULT CHARSET utf8mb4MySQL 回退到默認的 latin1。解決第一步確認 JdbcUtil 里的 URL 是否帶完整參數(shù)參考 4.1 節(jié)的代碼。第二步確認建庫腳本里寫了DEFAULT CHARSET utf8mb4。第三步如果已經(jīng)導(dǎo)入的庫字符集不對臨時補救可以執(zhí)行ALTER DATABASE supermarket CHARACTER SET utf8mb4;但最穩(wěn)的方式是刪庫重新導(dǎo)入一遍干凈的腳本。5.3 現(xiàn)象lib 目錄下有 jar 包但代碼里 import 不到類原因IDEA 里 jar 包放在WEB-INF/lib下方只是一個文件IDE 不會自動把它加入編譯路徑。這是 Maven 用戶最容易忽略的問題IDEA 對 Maven 依賴是自動識別的但對普通 lib 目錄不自動處理。解決在 Project Structure - Libraries 里把WEB-INF/lib整個目錄添加為 Java 庫。或者更簡單的做法在 IDEA 左側(cè)目錄樹里右鍵WEB-INF/lib選擇 Add as Library然后在彈窗里選 Project 級別確定后重新 Build。注意這一步必須在寫代碼之前完成否則每寫一行新引用都報紅。5.4 現(xiàn)象MySQL 8 連接報Public Key Retrieval is not allowed原因MySQL 8 默認使用caching_sha2_password身份認證JDBC 驅(qū)動第一次連接時拿不到服務(wù)端公鑰就會拋這個異常。任何項目里連接 MySQL 8 都會遇到和代碼本身無關(guān)。解決在 JDBC URL 末尾追加allowPublicKeyRetrievaltrue完整參數(shù)變成jdbc:mysql://localhost:3306/supermarket?useSSLfalseallowPublicKeyRetrievaltrueserverTimezoneAsia/Shanghai。這是驅(qū)動層面為本地連接提供的機制課設(shè)環(huán)境下允許這么做生產(chǎn)環(huán)境會用 SSL 證書替代。5.5 現(xiàn)象JSP 頁面里的${name}原樣輸出沒有被解析原因web.xml的版本聲明太舊解析到了 2.3。在 Servlet 2.3 規(guī)范中 EL 表達式默認不啟用${}會被當(dāng)成純文本輸出。Tomcat 也懶得管直接按舊規(guī)范處理頁面。解決把web.xml的根標(biāo)簽替換成 Servlet 4.0 規(guī)范的版本。在 IDEA 里打開WebContent/WEB-INF/web.xml將web-app根元素的版本和 schema 地址改掉web-app xmlnshttp://xmlns.jcp.org/xml/ns/javaee xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4.0.xsd version4.0如果你用的是注解方式WebServlet連web.xml都可以不聲明直接用注解。但為兼容一些老項目里的 Filter 配置web.xml 保留且版本寫高一些是更省事的做法。這五個問題基本覆蓋了課設(shè)運行階段 80% 的報錯。要是還遇上別的記住一個通用排除順序先看 IDEA 的控制臺報錯再去看 Tomcat 的日志最后看 MySQL 的字符集別跳層排查。6. 答辯前值得加的三處升級趨勢報表、接口返回 JSON 與自測腳本評分拿 95 分以上的項目除了基礎(chǔ)功能完整通常還要有兩三個“超出課設(shè)要求”的亮點。這里說三個改動量小、答辯價值高的方向。第一個是報表統(tǒng)計。用 SQL 按月份匯總訂單數(shù)量與銷售金額做成一個 JSP 柱狀圖或表格頁面。SQL 就三條SELECT DATE_FORMAT(create_time, %Y-%m) AS month, COUNT(*) AS order_count, SUM(total_amount) AS total_amount FROM order_info WHERE status IN (1, 2, 3) GROUP BY DATE_FORMAT(create_time, %Y-%m) ORDER BY month DESC;注意統(tǒng)計的是有效訂單狀態(tài) 0待審核和 4取消不應(yīng)該計入銷售額。答辯時能說出這層過濾邏輯比直接把所有訂單都加一遍要專業(yè)。第二個是把訂單接口改成返回 JSON給以后擴展留路。在 OrderServlet 里增加一個listJson方法返回application/json而不是轉(zhuǎn)發(fā)到 JSPpublic void listJson(HttpServletRequest req, HttpServletResponse resp) throws IOException { resp.setContentType(application/json;charsetutf-8); ListOrderInfo orders orderDao.findByCondition(null); resp.getWriter().write(new com.fasterxml.jackson.databind.ObjectMapper() .writeValueAsString(orders)); }頁面端以后想用 Vue 或原生 JS 重寫直接調(diào)這個接口就行Servlet 端不用動。這是典型的“前后端分離預(yù)留”寫法課設(shè)報告里加一句“系統(tǒng)支持以 JSON 方式對外提供數(shù)據(jù)接口”含金量立刻不一樣。第三個是自測腳本。課設(shè)答辯前用下面兩條命令快速檢查代碼質(zhì)量可以在三分鐘內(nèi)發(fā)現(xiàn)明顯的遺漏find src -name *.java | xargs wc -l | tail -1 grep -rn System.out.println src --include*.java | wc -l第一行統(tǒng)計 Java 源碼總行數(shù)你自己心里對項目規(guī)模有數(shù)第二行統(tǒng)計調(diào)試輸出個數(shù)答辯時被打斷切到代碼頁面會看到干凈的編程習(xí)慣。建議把第二行的結(jié)果壓到 0 或個位數(shù)排查用的輸出在提交前全部清理掉。以上三處改動加起來不會超過半天時間但答辯時的觀感完全不同。我當(dāng)年做課設(shè)時只顧著讓功能跑通沒注意編碼規(guī)范結(jié)果演示完被老師指著一處寫死的密碼問“這個怎么不加密”愣了幾秒只能承認偷懶。其實在提交前花十分鐘把敏感信息挪到常量類、把調(diào)試輸出清干凈就能避免這種尷尬。希望這篇能幫你少走這些彎路祝跑通順利。本文還有配套的精品資源點擊獲取