JavaWeb招聘網(wǎng)站實戰(zhàn):JSP+Servlet+MySQL從設(shè)計到部署)
簡介這是一份基于JavaWebJSPMySQL的BS架構(gòu)人才招聘網(wǎng)站設(shè)計與實現(xiàn)源碼包面向計算機專業(yè)畢業(yè)設(shè)計/課程設(shè)計學生以及想通過完整項目學習傳統(tǒng)Java Web開發(fā)的入門開發(fā)者。包內(nèi)含全套可運行源碼和詳細配套文檔覆蓋前臺首頁、網(wǎng)站導航、職位信息列表、用戶注冊、后臺登錄、職位信息管理、退出后臺管理等核心模塊并附帶SQL數(shù)據(jù)庫腳本便于本地部署和二次開發(fā)。壓縮包共930個文件以gif、js、html、jar、jsp、java等類型為主包含頁面交互、前端靜態(tài)資源、后端邏輯與依賴庫文件構(gòu)成直觀反映了經(jīng)典JSP項目的目錄規(guī)范整體約19.32MB。已有206人學習下載對希望快速掌握JSPServletMySQL開發(fā)流程的人來說是一份能直接運行、邊看文檔邊變實踐的優(yōu)質(zhì)參考。1. BS架構(gòu)下的人才招聘網(wǎng)站從“能跑”到“可以用”的JavaWeb系統(tǒng)用 JavaWeb JSP MySQL 構(gòu)建 BS 架構(gòu)的人才招聘網(wǎng)站是課設(shè)、畢設(shè)和中小企業(yè)快速搭建內(nèi)部招聘平臺最常見的組合。這套技術(shù)棧的意義在于瀏覽器只負責渲染 JSP 生成的 HTMLServlet 承擔請求分發(fā)與參數(shù)校驗MySQL 存放用戶、職位與投遞數(shù)據(jù)三層職責清晰。大多數(shù)拿到這個標題源碼包的人真正想做的事無非四類讀懂已有代碼、改掉一兩個 bug、換掉數(shù)據(jù)庫連接配置再順手做點二次功能開發(fā)。這個系統(tǒng)的體量恰好適合把 JSP 標簽、JDBC 封裝、Servlet 生命周期和 SQL 優(yōu)化串起來看一遍難度不大坑卻不少。2. 招聘網(wǎng)站的BS架構(gòu)設(shè)計與技術(shù)選型JSP、Servlet與MySQL的分層邊界2.1 為什么這門技術(shù)組合還值得在 BS 架構(gòu)里用BS 架構(gòu)的核心是客戶端無狀態(tài)、服務(wù)端統(tǒng)一管理會話與數(shù)據(jù)。JavaWebJSPMySQL 的好處在于JSP 負責動態(tài)渲染 HTML瀏覽器端不需要引入 Node 或 Webpack 那套構(gòu)建鏈。Servlet 負責接收 HTTP 請求并在服務(wù)端完成跳轉(zhuǎn)MySQL 負責把職位、簡歷、投遞記錄持久化。相比直接把 SQL 寫在 JSP 里這個組合最大的價值是 Servlet 作為“中間人”把業(yè)務(wù)邏輯從頁面里剝離開。這里要澄清一個常見誤區(qū)早期很多 JSP 項目把業(yè)務(wù)代碼全寫進% %腳本塊里頁面一改就觸發(fā) JSP 編譯錯誤維護成本非常高。如果自己從零寫至少拆成 Servlet DAO 兩層Servlet 只取參數(shù)、調(diào)服務(wù)、做跳轉(zhuǎn)DAO 負責 JDBC 數(shù)據(jù)訪問。這樣后面加“導出 Excel”或者“批量撤回投遞”這類功能時不用在十幾個 JSP 文件里翻找業(yè)務(wù)代碼。2.2 三層職責劃分與一次完整請求的流轉(zhuǎn)路徑一次“求職者搜索職位”的請求在各層的流轉(zhuǎn)很直白層次典型文件職責邊界表現(xiàn)層job_list.jsp、login.jsp接收表單數(shù)據(jù)渲染 HTML展示提示信息控制層JobListServlet、LoginServlet取參數(shù)、校驗入?yún)?、調(diào)用 DAO、跳轉(zhuǎn)或轉(zhuǎn)發(fā)數(shù)據(jù)層JobDao、UserDao、DBUtil執(zhí)行 SQL、封裝結(jié)果集、處理連接事務(wù)對應(yīng)到運行時請求路徑是瀏覽器提交GET /job/list?keywordjavapage2Tomcat 根據(jù) web.xml 里的映射找到 JobListServletServlet 解析 keyword 和 page調(diào)用 JobDao 執(zhí)行分頁 SQL把結(jié)果放進 request 域再forward到 job_list.jsp由 JSP 輸出完整 HTML。這里有個容易忽略的點重定向sendRedirect會丟 request 里的數(shù)據(jù)所以列表頁必須用 forward 轉(zhuǎn)發(fā)。2.3 項目結(jié)構(gòu) WEB-INF 約束與 JavaWeb 配置要點拿到源碼包先看目錄結(jié)構(gòu)判斷這個項目到底能不能擴展。合適的劃分是 controller、service、dao、model、util 五類包頁面單獨放 web 目錄下。我一般先找 WEB-INF/web.xml再確認頁面是否被 Servlet 轉(zhuǎn)發(fā)訪問。recruit-web/ ├── src/ │ ├── com/recruit/ │ │ ├── controller/ # Servlet 層接收請求并分發(fā) │ │ ├── service/ # 業(yè)務(wù)邏輯層處理事務(wù)和狀態(tài) │ │ ├── dao/ # JDBC 數(shù)據(jù)訪問SQL 集中管理 │ │ ├── model/ # 實體類對應(yīng)數(shù)據(jù)庫表字段 │ │ └── util/ # DBUtil、MD5Util、PageUtil │ └── db.properties # 數(shù)據(jù)庫連接配置 ├── web/ │ ├── WEB-INF/ │ │ ├── web.xml # Servlet 映射、過濾器、歡迎頁 │ │ └── lib/ # 第三方 jar 包 │ ├── jsp/ │ │ ├── job/ # 職位模塊頁面 │ │ ├── resume/ # 簡歷模塊頁面 │ │ └── admin/ # 后臺管理頁面 │ └── index.jspWEB-INF 下的 JSP 頁面不能被瀏覽器直接通過 URL 訪問只能經(jīng) Servlet forward 進入。所以很多新手把login.jsp放進 WEB-INF 后直接訪問就報 404這是 JavaWeb 配置階段最常見的錯誤。WEB-INF/lib 放的是 mysql-connector-java、jstl 這類依賴包用 Maven 構(gòu)建的話這里不需要手動放 jarwar 打包時會把依賴裝進來。JavaWeb 配置里另一個容易忽略的是編碼過濾器中文參數(shù)在 GET 和 POST 兩條路徑上的亂碼原因不一樣。POST 亂碼靠request.setCharacterEncoding(UTF-8)解決GET 請求是 Tomcat 在server.xml的 Connector 上解析 URI需要單獨加過濾器或改 URIEncoding。簡單做法是在 web.xml 里注冊 Tomcat 自帶的編碼過濾器url-pattern 設(shè)為/*同時 POST 和 GET 的亂碼都能兜住。3. MySQL數(shù)據(jù)庫建模招聘網(wǎng)站的表設(shè)計、索引與常用SQL3.1 用戶表、企業(yè)表與職位表的 DDL角色字段怎么定招聘網(wǎng)站的常見做法是六到八張表用戶表、企業(yè)表、職位表、簡歷表、投遞記錄表再加一張公告表。管理員、企業(yè)賬號、求職者賬號可以統(tǒng)一放進一張t_user用 role 字段區(qū)分不需要額外做“角色表 用戶角色中間表”。中間表在權(quán)限系統(tǒng)里是標準設(shè)計但在這里會讓登錄 SQL 多三次 JOIN性能和代碼復(fù)雜度都劃不來。CREATE TABLE t_user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL COMMENT 登錄名, password VARCHAR(64) NOT NULL COMMENT MD5摘要, role TINYINT NOT NULL DEFAULT 2 COMMENT 0管理員,1企業(yè),2求職者, company_id INT DEFAULT NULL COMMENT 企業(yè)賬號關(guān)聯(lián)公司, status TINYINT DEFAULT 1 COMMENT 1有效,0凍結(jié), created_at DATETIME DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY uk_username (username) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE t_company ( id INT PRIMARY KEY AUTO_INCREMENT, company_name VARCHAR(120) NOT NULL, industry VARCHAR(50) COMMENT 行業(yè), scale VARCHAR(20) COMMENT 規(guī)模, address VARCHAR(200), introduction TEXT ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE t_job ( id INT PRIMARY KEY AUTO_INCREMENT, company_id INT NOT NULL, title VARCHAR(100) NOT NULL COMMENT 職位名稱, salary_min INT COMMENT 薪資下限單位K, salary_max INT COMMENT 薪資上限單位K, city VARCHAR(50), edu_require VARCHAR(20) COMMENT 學歷要求, work_years VARCHAR(20) COMMENT 經(jīng)驗要求, description TEXT, status TINYINT DEFAULT 1 COMMENT 1招聘中,0下線, publish_time DATETIME DEFAULT CURRENT_TIMESTAMP, INDEX idx_company (company_id), INDEX idx_title (title), INDEX idx_publish (publish_time) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;角色用 TINYINT 而不是字符串占 1 字節(jié)查詢時用WHERE role 2比WHERE role jobseeker更快。企業(yè)用戶通過company_id關(guān)聯(lián)公司表職位表再通過company_id關(guān)聯(lián)公司這樣企業(yè)名稱只存一份不會出現(xiàn)職位列表里公司名各寫各的臟數(shù)據(jù)。這里故意沒有建物理外鍵刪除企業(yè)時外鍵會強制檢查子表手寫 SQL 的項目里順序一錯就報錯外鍵的維護成本大于收益關(guān)聯(lián)一致性靠業(yè)務(wù)層的 Service 保證。3.2 簡歷表與投遞記錄表多對多關(guān)系的落地寫法一個用戶可以有多份簡歷但簡化版系統(tǒng)通常只做一份主簡歷。簡歷表記錄教育背景、工作年限、期望薪資和技能描述用user_id指向用戶表。投遞記錄則是“簡歷”和“職位”之間的多對多關(guān)聯(lián)表除了兩個外鍵還帶狀態(tài)字段。CREATE TABLE t_resume ( id INT PRIMARY KEY AUTO_INCREMENT, user_id INT NOT NULL, real_name VARCHAR(50), phone VARCHAR(20), email VARCHAR(100), edu_degree VARCHAR(20) COMMENT 最高學歷, work_years TINYINT COMMENT 工作年限, expectation_city VARCHAR(50), expectation_salary VARCHAR(20), skill_desc TEXT, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, UNIQUE KEY uk_user (user_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE t_delivery ( id INT PRIMARY KEY AUTO_INCREMENT, resume_id INT NOT NULL, job_id INT NOT NULL, status TINYINT DEFAULT 0 COMMENT 0已投遞,1已查看,2邀請面試,3不合適, remark VARCHAR(200) COMMENT 面試備注或反饋, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, UNIQUE KEY uk_resume_job (resume_id, job_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;uk_resume_job這個唯一鍵是整個投遞邏輯的關(guān)鍵它保證同一份簡歷對同一個職位只能投一次。如果只在 Servlet 里先SELECT再INSERT并發(fā)請求下兩條線程都查不到記錄就會插出重復(fù)數(shù)據(jù)。有了唯一鍵兜底直接插入數(shù)據(jù)庫會拒掉第二條程序捕獲 Duplicate entry 異常后再拋提示即可。3.3 招聘網(wǎng)站高頻查詢 SQL模糊搜索、分頁與統(tǒng)計搜索職位最常用的語句是LIKE %關(guān)鍵詞%這個寫法在數(shù)據(jù)量小時沒什么感覺職位表到幾十萬行后前導百分號會讓索引失效全表掃描的耗時隨行數(shù)線性膨脹。先記住一個結(jié)論對小型招聘網(wǎng)站正確做法是控制 JOIN 范圍、給常用過濾字段建聯(lián)合索引而不是一上來就上全文檢索。SELECT j.id, j.title, j.salary_min, j.salary_max, j.city, c.company_name, j.publish_time FROM t_job j INNER JOIN t_company c ON j.company_id c.id WHERE j.status 1 AND (j.title LIKE CONCAT(%, ?, %) OR c.company_name LIKE CONCAT(%, ?, %)) AND (? OR j.city ?) ORDER BY j.publish_time DESC LIMIT ?, ?;四個?分別是 keyword、keyword、city、city最后兩個參數(shù)是 offset 和 pageSize。LIMIT的 offset 不能在 Servlet 里直接拿用戶傳的頁碼當參數(shù)正確算法是offset (page - 1) * size。城市條件寫成(? OR j.city ?)比拼接兩個 SQL 更干凈DAO 層只需要傳同一個 session 字符串。統(tǒng)計企業(yè)收到的投遞量是管理后臺的常用功能。這里用LEFT JOIN而不是INNER JOIN因為招聘中的企業(yè)即使一條投遞都沒有也要顯示 0而不是被過濾掉。SELECT j.company_id, c.company_name, COUNT(d.id) AS delivery_count FROM t_job j LEFT JOIN t_delivery d ON j.id d.job_id LEFT JOIN t_company c ON j.company_id c.id WHERE j.status 1 GROUP BY j.company_id, c.company_name ORDER BY delivery_count DESC;COUNT(d.id)只統(tǒng)計真實存在的投遞記錄如果t_delivery沒有匹配行左邊記錄也會保留COUNT 結(jié)果是 0。GROUP BY 后面帶的列要和 SELECT 中非聚合列一致否則在高版本 MySQL 的 ONLY_FULL_GROUP_BY 模式下會直接報語法錯誤。4. JSPServlet實現(xiàn)核心業(yè)務(wù)鏈路登錄、職位分頁與簡歷投遞4.1 登錄認證的 Servlet 處理與 Session 生命周期登錄邏輯是理解這套系統(tǒng)最適合的入口它把請求參數(shù)、DAO 查詢、Session 存儲和頁面跳轉(zhuǎn)串在一起。先看最常見的 Servlet 寫法再補一個登錄檢查的過濾器。WebServlet(/login) public class LoginServlet extends HttpServlet { private UserDao userDao new UserDao(); Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding(UTF-8); response.setContentType(text/html;charsetUTF-8); String username request.getParameter(username); String password MD5Util.md5(request.getParameter(password)); String role request.getParameter(role); User user userDao.findByLogin(username, password, role); if (user ! null) { HttpSession session request.getSession(); session.setAttribute(loginUser, user); session.setMaxInactiveInterval(30 * 60); String target 2.equals(role) ? index.jsp : admin/index.jsp; response.sendRedirect(target); } else { response.getWriter().write( scriptalert(用戶名或密碼錯誤);history.back();/script); } } }登錄前就做 md5 摘要避免明文密碼進入 SQL 語句也避免日志或異常堆棧里帶出密碼。role參數(shù)來自登錄表單的下拉框找到用戶后要及時把用戶對象放進 Session后續(xù)所有頁面都從 Session 里取當前登錄人。登錄成功后用sendRedirect而不是 forward是為了讓瀏覽器地址欄變成目標頁刷新時不會再次提交表單避免每次刷新都彈“重新發(fā)送表單”。登錄檢查 Filter 是第二道防線如果用戶沒有登錄就訪問受保護目錄直接重定向到登錄頁。一個過濾器能讓整站受保護不需要每個 Servlet 都重復(fù)判斷 Session 是否為空。public class LoginFilter implements Filter { Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { HttpServletRequest request (HttpServletRequest) req; HttpServletResponse response (HttpServletResponse) resp; Object loginUser request.getSession().getAttribute(loginUser); if (loginUser null) { response.sendRedirect(request.getContextPath() /login.jsp); return; } chain.doFilter(request, response); } }過濾器最常踩的坑是放行規(guī)則沒配好。靜態(tài)資源、登錄頁和登錄 Servlet 需要排除在外否則正常用戶也會被攔回來。url-pattern用/job/*、/resume/*這類精確目錄范圍比用/*到處放行更安全。4.2 職位列表的分頁查詢與 JSP 渲染參數(shù)計算職位列表頁是招聘網(wǎng)站的流量入口涉及三個要素頁碼解析、DAO 分頁查詢、JSP 端循環(huán)輸出。先看控制層的分頁代碼。WebServlet(/job/list) public class JobListServlet extends HttpServlet { private JobDao jobDao new JobDao(); Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String keyword request.getParameter(keyword); String pageStr request.getParameter(page); String sizeStr request.getParameter(size); int page pageStr null ? 1 : Integer.parseInt(pageStr); int size sizeStr null ? 8 : Integer.parseInt(sizeStr); // 只允許正向翻頁防止負數(shù)頁碼把 offset 算成負數(shù) page Math.max(page, 1); int offset (page - 1) * size; ListJobVO list jobDao.queryPage(keyword, city, offset, size); int total jobDao.countPage(keyword, city); int totalPages (total size - 1) / size; // 超過最大頁時回退到最后一頁避免空列表 if (page totalPages) { page totalPages; offset (page - 1) * size; list jobDao.queryPage(keyword, city, offset, size); } request.setAttribute(jobList, list); request.setAttribute(currentPage, page); request.setAttribute(totalPages, totalPages); request.getRequestDispatcher(/jsp/job/list.jsp).forward(request, response); } }分頁參數(shù)計算容易出錯的地方是兩個用戶直接手改 URL 傳page0或page10000第一種用Math.max(page, 1)擋掉第二種在查出總頁數(shù)后再做一次越界回退。查詢列表和查詢總數(shù)必須用同一組 WHERE 條件否則頁數(shù)對不上。JSP 側(cè)用 JSTL 的c:forEach渲染避免在頁面里寫 Java 腳本塊。c:if 控制上一頁下一頁是否顯示需要往前端模板傳當前頁和總頁數(shù)。c:forEach items${jobList} varjob div classjob-item h3a href${pageContext.request.contextPath}/job/detail?id${job.id} ${job.title}/a/h3 p${job.companyName} | ${job.city} | ${job.salaryMin}K-${job.salaryMax}K/p a classbtn href${ctx}/delivery/add?jobId${job.id}立即投遞/a /div /c:forEach c:if test${currentPage 1} a href${ctx}/job/list?page${currentPage - 1}上一頁/a /c:if span第 ${currentPage} / ${totalPages} 頁/span c:if test${currentPage totalPages} a href${ctx}/job/list?page${currentPage 1}下一頁/a /c:if${ctx}已經(jīng)在 Servlet 或頁頭用pageContext.request.contextPath注入過作用是拼出項目上下文根路徑。直接寫/job/list在部署到根目錄時沒問題一旦 WAR 包改名后發(fā)布到同端口硬編碼路徑就會 404。頁面里不要直接訪問job對象的內(nèi)部字段JSP 中通過 EL 表達式調(diào)用的是 getter 方法字段名大小寫寫錯會直接報 PropertyNotFoundException。4.3 簡歷投遞的冪等控制與不可重復(fù)投遞提示投遞動作由一個 Servlet 接收請求檢查登錄態(tài)和簡歷完整性最后調(diào)用 DAO 插入投遞記錄。這里需要同時處理兩個問題用戶沒登錄直接投遞、用戶重復(fù)投遞。先寫 DAO 層。public boolean addDelivery(int resumeId, int jobId) { String sql INSERT INTO t_delivery (resume_id, job_id) VALUES (?, ?); try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setInt(1, resumeId); ps.setInt(2, jobId); return ps.executeUpdate() 0; } catch (SQLException e) { if (e.getErrorCode() 1062) { // 1062 是 MySQL 的唯一索引沖突錯誤碼說明重復(fù)投遞 return false; } e.printStackTrace(); return false; } }常見錯誤碼含義本項目中對應(yīng)場景1045數(shù)據(jù)庫用戶名或密碼錯誤db.properties 配置錯1062唯一鍵沖突同一簡歷重復(fù)投遞同一職位1049數(shù)據(jù)庫不存在建庫腳本沒執(zhí)行這套代碼并不需要進入數(shù)據(jù)庫先查一次再說INSERT 唯一鍵沖突時直接返回 false使并發(fā)投遞時也只有一條能成功這就是 MySQL 鎖表之外更輕量的冪等方案。Comparator和頁面層檢查 Session 為空時跳登錄頁簡歷不存在時跳編輯頁DAO 返回 false 時用history.back()提示不可重復(fù)投遞配合 alert 彈窗處理。5. 部署到Tomcat的版本選擇與上線驗證技巧5.1 WAR 包構(gòu)建與 Tomcat 9 / 10 版本差異項目在本機跑通和正式部署之間隔著一個版本兼容問題。這套 JSP Servlet 源碼如果用的是javax.servlet包名建議直接選 Tomcat 9。Tomcat 10 之后把命名空間遷到了jakarta.servlet老代碼編譯通過但運行時報ClassNotFoundException這是從黑馬 JavaWeb 筆記到實際部署最常見的錯位點。# Maven 打 war 包并跳過測試 mvn clean package -DskipTests # 拷貝到 Tomcat webapps 目錄 cp target/recruit.war /opt/tomcat/webapps/ # 啟動 /opt/tomcat/bin/startup.sh tail -f /opt/tomcat/logs/catalina.outWAR 包部署后 Tomcat 會自動解壓出recruit/目錄瀏覽器訪問路徑是http://IP:8080/recruit/。如果改完 JSP 后頁面不生效先刪掉tomcat/work/Catalina/localhost/recruit/下的 JSP 編譯緩存目錄這相當于前端強刷之外的 JSP 緩存層。5.2 用一個 bat 腳本帶走 MySQL 每日業(yè)務(wù)數(shù)據(jù)部署在 Windows 服務(wù)器上的小型招聘網(wǎng)站最靠譜的備份方案不是裝第三方客戶端而是用計劃任務(wù)每天跑一次 mysqldump。這樣即使第二天誤刪數(shù)據(jù)也能在幾分鐘內(nèi)恢復(fù)。echo off set MYSQL_HOMEC:\mysql-8.0.36-winx64\bin set BACKUP_DIRD:\backup\recruit_db set DB_NAMErecruit_db set DB_USERroot set DB_PASS123456 set YYYYMMDD%date:~0,4%%date:~5,2%%date:~8,2% %MYSQL_HOME%\mysqldump -u%DB_USER% -p%DB_PASS% --default-character-setutf8mb4 %DB_NAME% %BACKUP_DIR%\recruit_%YYYYMMDD%.sql forfiles /p %BACKUP_DIR% /m *.sql /d -15 /c cmd /c del path前兩行 set 是 MySQL 安裝目錄和備份目錄按實際環(huán)境改。%date:~0,4%取當前日期前四位做年份后面的~5,2和~8,2取月和日前提是系統(tǒng)日期格式為yyyy-MM-dd。首次運行前先執(zhí)行echo %date%驗證一下區(qū)域設(shè)置的格式不同取子串的位置要對齊。最后一行保留最近 15 天備份防止磁盤被 sql 文件塞滿。執(zhí)行這一腳本時遇到 1045 錯誤先檢查賬號密碼是否在命令行里有特殊字符備份出的文件用show tables;快速驗證是否完整再考慮加入 Windows 任務(wù)計劃。5.3 上線后的三條驗證路徑部署完成后別急著點功能按順序走三條檢查路徑。第一次檢查 Tomcat 日志與 JSP 編譯產(chǎn)物重啟后訪問首頁并觀察 work 目錄下是否生成了對應(yīng)的 class 文件第二次用 Navicat 或命令行執(zhí)行SHOW INDEX FROM t_delivery;確認唯一鍵和常用查詢的索引都存在第三次用兩個不同瀏覽器同時登錄驗證 Session 不會互相踢掉再在其中一個窗口連續(xù)點擊兩次投遞按鈕確認第二條請求被攔截。做完這三項整個系統(tǒng)從數(shù)據(jù)庫到頁面才算真正連通。本文還有配套的精品資源點擊獲取