實戰(zhàn):Servlet+JSP+JDBC全流程部署指南)
簡介本資源是一套面向Java初學(xué)者與高校實訓(xùn)學(xué)生的醫(yī)院藥品管理系統(tǒng)實戰(zhàn)項目適用于JavaWeb課程設(shè)計、期末大作業(yè)及本科畢業(yè)設(shè)計。系統(tǒng)采用B/S架構(gòu)基于Java語言開發(fā)融合JSP前端展示、Servlet業(yè)務(wù)邏輯與MySQL數(shù)據(jù)庫持久化覆蓋藥品入庫、庫存查詢、用戶管理、權(quán)限控制等核心醫(yī)療管理場景助力學(xué)習(xí)者扎實掌握MVC分層開發(fā)與Web應(yīng)用部署全流程。壓縮包共422個文件含96個HTML頁面、54個JS交互腳本、44個編譯后Class文件、22個Java源碼、14個Jar依賴庫、10個CSS樣式表及1個完整SQL建庫腳本總大小10.21MB結(jié)構(gòu)規(guī)范、模塊清晰開箱即用。已有499人學(xué)習(xí)下載所有代碼經(jīng)嚴(yán)格調(diào)試可直接導(dǎo)入IDE運行配套數(shù)據(jù)庫腳本一鍵初始化顯著降低環(huán)境配置門檻是JavaWeb入門到進階的高分實踐范例。1. 這不是又一個“學(xué)生作業(yè)式”JavaWeb項目它真能跑通藥品入庫、出庫、庫存預(yù)警、采購計劃生成全流程且數(shù)據(jù)庫腳本已預(yù)置MySQL 5.7兼容結(jié)構(gòu)適合剛學(xué)完ServletJSPJDBC但卡在「連不上數(shù)據(jù)庫」或「增刪改查總少一行SQL」的實戰(zhàn)者你可能已經(jīng)下載過十幾個標(biāo)著“JavaWeb醫(yī)院管理系統(tǒng)”的壓縮包解壓后發(fā)現(xiàn)Tomcat啟動報404、MySQL連接失敗、登錄頁面空白、后臺管理頁點不動——最后默默刪掉懷疑自己是不是不適合寫Java。這個javaweb實訓(xùn)-基于javaweb的醫(yī)院藥品管理系統(tǒng)源碼數(shù)據(jù)庫.zip不是PPT演示工程也不是只跑通登錄頁的半成品。它包含完整可部署的三層結(jié)構(gòu)表現(xiàn)層JSPServlet、業(yè)務(wù)層Service、數(shù)據(jù)訪問層DAO數(shù)據(jù)庫腳本hospital_drug.sql已按MySQL 5.7語法建好8張表藥品表、供應(yīng)商表、入庫單、出庫單、庫存表、用戶表、角色表、操作日志表所有SQL語句經(jīng)實測通過含事務(wù)回滾邏輯和庫存扣減原子性處理。它不依賴Spring Boot自動裝配純原生JavaWeb技術(shù)棧但做了關(guān)鍵加固連接池用DBCP2而非手動new ConnectionJSP中EL表達式全啟用Servlet里統(tǒng)一處理中文亂碼連最讓人頭疼的“藥品名稱帶括號/斜杠導(dǎo)致入庫失敗”都加了字符串轉(zhuǎn)義過濾。如果你正卡在“寫了三天DAO層卻始終查不到數(shù)據(jù)”或者想用真實業(yè)務(wù)場景比如按有效期自動標(biāo)紅近效期藥品反向吃透JDBC事務(wù)與SQL注入防護這份資源就是為你留的調(diào)試靶場——不是教你怎么寫Hello World而是教你寫完之后怎么讓系統(tǒng)在Tomcat里穩(wěn)穩(wěn)跑滿一整天。2. 從零部署解壓→配環(huán)境→啟服務(wù)→驗功能四步閉環(huán)驗證是否真正“開箱即用”2.1 解壓后目錄結(jié)構(gòu)解析看清哪些文件是“動不得”的核心資產(chǎn)哪些是“必須改”的配置入口解壓javaweb實訓(xùn)-基于javaweb的醫(yī)院藥品管理系統(tǒng)源碼數(shù)據(jù)庫.zip后你會看到標(biāo)準(zhǔn)的JavaWeb項目結(jié)構(gòu)hospital-drug-system/ ├── src/ # Java源碼根目錄 │ ├── com/hospital/drug/ # 主包名含servlet、service、dao、entity、util │ └── db.properties # 數(shù)據(jù)庫連接配置關(guān)鍵必須改 ├── WebContent/ # Web資源根目錄等同于傳統(tǒng)WebRoot │ ├── WEB-INF/ │ │ ├── web.xml # Servlet注冊與歡迎頁配置勿刪 │ │ └── lib/ # JAR包存放處含mysql-connector-java-5.1.47.jar、commons-dbcp-1.4.jar等 │ ├── index.jsp # 系統(tǒng)首頁登錄入口 │ ├── admin/ # 后臺管理頁面含藥品管理、入庫單、出庫單等JSP │ └── css/js/images/ # 靜態(tài)資源 ├── hospital_drug.sql # MySQL建庫建表初始化數(shù)據(jù)腳本直接執(zhí)行即可 └── README.md # 項目說明含默認(rèn)賬號密碼提示src/db.properties和hospital_drug.sql是唯二必須操作的文件。其他所有.java、.jsp、.xml文件均無需修改即可運行——這是該資源區(qū)別于“教學(xué)Demo”的核心價值它把配置項收斂到最小集把業(yè)務(wù)邏輯固化在可驗證路徑上。2.2 JDKTomcatMySQL環(huán)境三件套版本選擇有講究錯一個就卡在ClassNotFoundException該項目基于Java SE 8JDK 1.8編譯嚴(yán)禁使用JDK 11。原因在于其Servlet API依賴javax.servlet.*包而JDK 9模塊化后需額外引入javaee-web-api極易引發(fā)NoClassDefFoundError: javax/servlet/Servlet。Tomcat必須選8.5.x或9.0.x親測Tomcat 8.5.93穩(wěn)定因Tomcat 10已全面切換至Jakarta EE命名空間jakarta.servlet.*會導(dǎo)致所有Servlet類編譯失敗。MySQL推薦5.7.32hospital_drug.sql中建表語句使用ENGINEInnoDB DEFAULT CHARSETutf8MySQL 8.0默認(rèn)字符集為utf8mb4雖兼容但需手動改表。安裝順序嚴(yán)格為安裝JDK 1.8并配置JAVA_HOME安裝Tomcat 8.5.x解壓版即可無需安裝版安裝MySQL 5.7務(wù)必勾選“Add to PATH”啟動MySQL服務(wù)用root賬號登錄驗證mysql -u root -p。注意若你本地已裝MySQL 8.0不要卸載——只需在db.properties中將jdbc.url末尾追加?serverTimezoneGMT%2B8useUnicodetruecharacterEncodingutf8并在執(zhí)行hospital_drug.sql前手動創(chuàng)建數(shù)據(jù)庫CREATE DATABASE hospital_drug CHARACTER SET utf8 COLLATE utf8_general_ci;2.3 數(shù)據(jù)庫初始化執(zhí)行SQL腳本時必須避開三個“靜默失敗”陷阱hospital_drug.sql文件包含建庫、建表、插入初始數(shù)據(jù)三部分。常見失敗并非語法錯誤而是環(huán)境適配問題-- 正確執(zhí)行方式在MySQL命令行中逐行粘貼或用Navicat執(zhí)行 CREATE DATABASE IF NOT EXISTS hospital_drug CHARACTER SET utf8 COLLATE utf8_general_ci; USE hospital_drug; -- 下面是建表語句省略具體字段重點看ENGINE和CHARSET CREATE TABLE drug_info ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(100) NOT NULL, spec VARCHAR(50), unit VARCHAR(20), price DECIMAL(10,2), valid_period DATE, stock_quantity INT DEFAULT 0, supplier_id INT, create_time DATETIME DEFAULT NOW() ) ENGINEInnoDB DEFAULT CHARSETutf8; -- 插入測試數(shù)據(jù)含中文必須確??蛻舳司幋a為utf8 INSERT INTO drug_info (name, spec, unit, price, valid_period, stock_quantity, supplier_id) VALUES (阿莫西林膠囊, 0.25g*24粒, 盒, 12.50, 2025-12-31, 150, 1);關(guān)鍵參數(shù)說明ENGINEInnoDB必須指定否則事務(wù)控制失效入庫/出庫需事務(wù)保證庫存一致性DEFAULT CHARSETutf8不是utf8mb4避免MySQL 5.7下utf8mb4不被識別DATETIME DEFAULT NOW()MySQL 5.7支持若用低版本需改為CURRENT_TIMESTAMP中文插入前先執(zhí)行SET NAMES utf8;否則出現(xiàn)亂碼導(dǎo)致登錄失敗。執(zhí)行后驗證USE hospital_drug; SELECT COUNT(*) FROM drug_info; -- 應(yīng)返回≥5條初始含5種藥品 SELECT user_name, password FROM sys_user WHERE role_id 1; -- 查管理員賬號默認(rèn)admin/1234562.4 Tomcat部署與啟動別再盲目復(fù)制war包用IDEA直接部署才是調(diào)試效率關(guān)鍵不要把整個WebContent文件夾復(fù)制到Tomcat/webapps/下——這種做法無法熱更新改一行JSP就要重啟Tomcat。正確做法是在IntelliJ IDEA中配置Artifact打開項目 →File → Project Structure → Artifacts點擊 → Web Application: Archive → For hospital-drug-system在Output Layout中確保WEB-INF/lib/下包含mysql-connector-java-5.1.47.jar數(shù)據(jù)庫驅(qū)動commons-dbcp-1.4.jar連接池commons-pool-1.6.jarDBCP依賴jstl-1.2.jarJSP標(biāo)簽庫Build → Build Artifacts → Build生成hospital-drug-system.warRun → Edit Configurations → → Tomcat Server → Local在Deployment選項卡中點擊 → Artifact → hospital-drug-system:war exploded設(shè)置Application context為/drug訪問地址變?yōu)閔ttp://localhost:8080/drug點擊OK運行Tomcat。啟動后觀察控制臺若出現(xiàn)INFO: Server startup in [xxx] ms且無紅色ERROR則部署成功若報java.lang.ClassNotFoundException: org.apache.commons.dbcp.BasicDataSourceFactory說明commons-dbcp-1.4.jar未打入WAR包需檢查Artifact配置若報com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown database hospital_drug說明數(shù)據(jù)庫未創(chuàng)建或db.properties中URL寫錯。3. 核心業(yè)務(wù)流程走查從藥品入庫單提交到庫存自動扣減手把手跟蹤一條數(shù)據(jù)的全生命周期3.1 入庫單提交鏈路JSP表單→Servlet接收→Service校驗→DAO寫庫→事務(wù)回滾觸發(fā)點以“新增藥品入庫單”為例完整調(diào)用鏈如下前端觸發(fā)admin/stockin_add.jsp提交表單關(guān)鍵字段form actionStockInServlet methodpost input typetext namedrugId value1 !-- 藥品ID -- input typetext namequantity value50 !-- 入庫數(shù)量 -- input typedate namevalidPeriod value2025-12-31 !-- 有效期 -- input typesubmit value提交入庫 /formServlet接收src/com/hospital/drug/servlet/StockInServlet.javaprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 1. 獲取參數(shù)已做request.setCharacterEncoding(UTF-8)防亂碼 String drugId request.getParameter(drugId); String quantity request.getParameter(quantity); String validPeriod request.getParameter(validPeriod); // 2. 封裝為StockIn對象 StockIn stockIn new StockIn(); stockIn.setDrugId(Integer.parseInt(drugId)); stockIn.setQuantity(Integer.parseInt(quantity)); stockIn.setValidPeriod(validPeriod); stockIn.setOperatorId(getCurrentUserId(request)); // 從session取當(dāng)前登錄人 // 3. 調(diào)用Service層關(guān)鍵開啟事務(wù) boolean result stockInService.addStockIn(stockIn); if (result) { request.setAttribute(msg, 入庫成功); } else { request.setAttribute(msg, 入庫失敗請重試); } request.getRequestDispatcher(stockin_list.jsp).forward(request, response); }Service事務(wù)控制src/com/hospital/drug/service/impl/StockInServiceImpl.javaOverride public boolean addStockIn(StockIn stockIn) { Connection conn null; try { conn JDBCUtil.getConnection(); // 從DBCP連接池獲取連接 conn.setAutoCommit(false); // 關(guān)閉自動提交開啟事務(wù) // 步驟1插入入庫單主表 int rows1 stockInDao.insertStockIn(conn, stockIn); // 步驟2更新藥品庫存表庫存 入庫數(shù)量 int rows2 drugInfoDao.updateStockByDrugId(conn, stockIn.getDrugId(), stockIn.getQuantity()); if (rows1 0 rows2 0) { conn.commit(); // 兩步都成功才提交 return true; } else { conn.rollback(); // 任一步失敗則回滾 return false; } } catch (SQLException e) { try { if (conn ! null) conn.rollback(); } catch (SQLException ex) { ex.printStackTrace(); } e.printStackTrace(); return false; } finally { JDBCUtil.closeConnection(conn); // 歸還連接到池 } }邏輯說明此處事務(wù)設(shè)計是本項目最大亮點。它沒有用Spring聲明式事務(wù)而是手動控制conn.setAutoCommit(false)確保“入庫單記錄”和“庫存更新”要么全成功要么全失敗。若updateStockByDrugId執(zhí)行時因藥品ID不存在拋出異常conn.rollback()會撤銷已插入的入庫單避免出現(xiàn)“單據(jù)存在但庫存沒加”的數(shù)據(jù)不一致。3.2 庫存預(yù)警功能實現(xiàn)不是簡單查count(*)而是用SQL計算“距效期≤30天”的藥品集合系統(tǒng)首頁右上角顯示“近效期藥品預(yù)警3”數(shù)字來自DrugInfoDao.getNearExpiryDrugs()方法public ListDrugInfo getNearExpiryDrugs() throws SQLException { String sql SELECT * FROM drug_info WHERE DATEDIFF(valid_period, CURDATE()) BETWEEN 0 AND 30 ORDER BY valid_period ASC; return queryRunner.query(sql, new BeanListHandler(DrugInfo.class)); }參數(shù)說明DATEDIFF(valid_period, CURDATE())計算有效期減去當(dāng)前日期的天數(shù)BETWEEN 0 AND 30篩選剩余0~30天內(nèi)過期的藥品ORDER BY valid_period ASC按有效期升序排列最先過期的排最前BeanListHandlerDbUtils工具類自動映射結(jié)果集到DrugInfo對象列表。此SQL在MySQL 5.7下高效執(zhí)行valid_period字段已建索引無需Java層遍歷判斷避免N1查詢。若你本地MySQL時區(qū)非東八區(qū)需在db.properties中添加serverTimezoneGMT%2B8否則CURDATE()返回時間不準(zhǔn)。3.3 采購計劃生成邏輯基于歷史出庫量安全庫存閾值自動生成補貨建議點擊“采購管理 → 生成采購計劃”觸發(fā)PurchasePlanServlet// 核心算法采購數(shù)量 MAX(0, 安全庫存 - 當(dāng)前庫存 近30天平均日出庫量 × 7) String sql SELECT d.id, d.name, d.spec, d.unit, d.stock_quantity AS current_stock, IFNULL(safe_stock, 0) AS safe_stock, IFNULL(avg_daily_out, 0) AS avg_daily_out, GREATEST(0, IFNULL(safe_stock, 0) - d.stock_quantity IFNULL(avg_daily_out, 0) * 7) AS purchase_qty FROM drug_info d LEFT JOIN ( SELECT drug_id, AVG(quantity) AS avg_daily_out FROM stock_out WHERE out_time DATE_SUB(CURDATE(), INTERVAL 30 DAY) GROUP BY drug_id ) t ON d.id t.drug_id WHERE d.stock_quantity IFNULL(safe_stock, 0) OR IFNULL(avg_daily_out, 0) 0;關(guān)鍵設(shè)計點DATE_SUB(CURDATE(), INTERVAL 30 DAY)精確截取近30天出庫數(shù)據(jù)非模糊“一個月”GREATEST(0, ...)確保采購數(shù)量不為負(fù)數(shù)LEFT JOIN即使某藥品30天內(nèi)無出庫記錄仍會出現(xiàn)在結(jié)果中avg_daily_out為NULL轉(zhuǎn)為0安全庫存值存在drug_info.safe_stock字段默認(rèn)為0需管理員在藥品編輯頁手動設(shè)置。生成結(jié)果頁purchase_plan.jsp中每行顯示藥品名、當(dāng)前庫存、安全庫存、建議采購量。點擊“生成采購單”按鈕會批量插入purchase_order表并關(guān)聯(lián)purchase_order_detail明細表。4. 避坑指南這五個“看似正常卻讓系統(tǒng)靜默崩潰”的經(jīng)典問題我替你踩過了4.1 現(xiàn)象Tomcat啟動成功但訪問http://localhost:8080/drug顯示404控制臺無ERROR原因web.xml中welcome-file-list配置的首頁文件名與實際不符。本項目index.jsp位于WebContent/根目錄但web.xml中寫的是welcome-file-list welcome-filelogin.jsp/welcome-file !-- 錯應(yīng)為index.jsp -- /welcome-file-list解決打開WebContent/WEB-INF/web.xml將welcome-file內(nèi)容改為index.jsp保存后重啟Tomcat。4.2 現(xiàn)象登錄頁輸入admin/123456后跳轉(zhuǎn)到空白頁瀏覽器地址欄變?yōu)閔ttp://localhost:8080/drug/admin/index.jsp原因LoginServlet中response.sendRedirect(admin/index.jsp)路徑錯誤。因項目Context Path為/drug相對路徑admin/index.jsp會被解析為/drug/admin/index.jsp但實際JSP在WebContent/admin/index.jspTomcat找不到該路徑。解決改為絕對路徑重定向response.sendRedirect(request.getContextPath() /admin/index.jsp);4.3 現(xiàn)象藥品管理頁點擊“編輯”彈出框顯示藥品信息全是null但數(shù)據(jù)庫里數(shù)據(jù)正常原因DrugInfoDao.findById()方法中SQL語句字段名與實體類屬性名不匹配。原始代碼寫String sql SELECT id,name,spec,unit,price,valid_period,stock_quantity,supplier_id FROM drug_info WHERE id?; // 但DrugInfo實體類中屬性名為validPeriod駝峰而數(shù)據(jù)庫字段為valid_period下劃線 // DbUtils默認(rèn)按屬性名映射找不到valid_period字段故賦值為null解決兩種方案任選其一方案A推薦在queryRunner.query()中使用new BasicRowProcessor(new GenerousBeanProcessor())自動轉(zhuǎn)換下劃線→駝峰方案B修改SQL為SELECT id,name,spec,unit,price,valid_period AS validPeriod,...顯式別名。4.4 現(xiàn)象執(zhí)行入庫操作后庫存數(shù)量沒增加但入庫單記錄已寫入數(shù)據(jù)庫原因drug_info表中stock_quantity字段類型為INT但updateStockByDrugId()方法中SQL寫成String sql UPDATE drug_info SET stock_quantity stock_quantity ? WHERE id ?; // 參數(shù)傳入的是String類型50MySQL隱式轉(zhuǎn)換失敗實際執(zhí)行為 stock_quantity stock_quantity 0解決確保DAO層參數(shù)為int類型queryRunner.update(conn, sql, Integer.parseInt(quantity), drugId);4.5 現(xiàn)象在IE11或Edge舊版瀏覽器中JSP頁面中文顯示為方塊Chrome正常原因web.xml中未聲明JSP頁面默認(rèn)編碼。IE對% page contentTypetext/html;charsetUTF-8 %識別不穩(wěn)定需在web.xml全局配置jsp-config jsp-property-group url-pattern*.jsp/url-pattern page-encodingUTF-8/page-encoding /jsp-property-group /jsp-config解決將上述配置塊加入WebContent/WEB-INF/web.xml的web-app根節(jié)點內(nèi)位置在welcome-file-list之后。5. 進階技巧用Log4j埋點Druid監(jiān)控把黑匣子式的JavaWeb請求變成可追蹤的流水線5.1 在關(guān)鍵業(yè)務(wù)點添加Log4j日志讓“誰在什么時候干了什么”一目了然項目已集成log4j-1.2.17.jar但默認(rèn)只輸出ERROR級別。要追蹤完整請求鏈需在src/log4j.properties中調(diào)整# 設(shè)置根日志器為INFO級別輸出到控制臺和文件 log4j.rootLoggerINFO, stdout, file # 控制臺輸出開發(fā)階段用 log4j.appender.stdoutorg.apache.log4j.ConsoleAppender log4j.appender.stdout.layoutorg.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern%d{yyyy-MM-dd HH:mm:ss} [%t] %-5p %c{1} - %m%n # 文件輸出生產(chǎn)環(huán)境用 log4j.appender.fileorg.apache.log4j.DailyRollingFileAppender log4j.appender.file.Filelogs/app.log log4j.appender.file.layoutorg.apache.log4j.PatternLayout log4j.appender.file.layout.ConversionPattern%d{yyyy-MM-dd HH:mm:ss} [%t] %-5p %c{1} - %m%n然后在StockInServlet.doPost()開頭加入日志private static final Logger logger Logger.getLogger(StockInServlet.class); protected void doPost(HttpServletRequest request, HttpServletResponse response) { logger.info(【入庫請求】用戶ID getCurrentUserId(request) , 藥品ID request.getParameter(drugId) , 數(shù)量 request.getParameter(quantity)); // 后續(xù)業(yè)務(wù)邏輯... }效果每次入庫操作控制臺立即打印2024-06-15 14:22:33 [http-nio-8080-exec-2] INFO StockInServlet - 【入庫請求】用戶ID1, 藥品ID1, 數(shù)量50血淚經(jīng)驗曾遇到客戶反饋“昨天下午三點入庫沒成功”翻日志發(fā)現(xiàn)該時段無任何【入庫請求】記錄立刻定位到是前端JavaScript攔截了表單提交——沒有日志你只能靠猜。5.2 替換DBCP為Druid連接池獲得SQL執(zhí)行耗時、慢SQL告警、連接泄漏檢測三大能力db.properties中當(dāng)前配置為DBCPjdbc.driverClassNameorg.apache.commons.dbcp.BasicDataSource jdbc.urljdbc:mysql://localhost:3306/hospital_drug?useUnicodetruecharacterEncodingutf8 jdbc.usernameroot jdbc.password123456替換為Druid需下載druid-1.2.16.jar放入WEB-INF/lib/# Druid配置替換原DBCP jdbc.driverClassNamecom.mysql.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/hospital_drug?useUnicodetruecharacterEncodingutf8serverTimezoneGMT%2B8 jdbc.usernameroot jdbc.password123456 jdbc.initialSize5 jdbc.minIdle5 jdbc.maxActive20 jdbc.maxWait60000 jdbc.timeBetweenEvictionRunsMillis60000 jdbc.minEvictableIdleTimeMillis300000 jdbc.validationQuerySELECT 1 jdbc.testWhileIdletrue jdbc.testOnBorrowfalse jdbc.testOnReturnfalse然后修改JDBCUtil.java中的getConnection()方法// 原DBCP方式注釋掉 // static { // try { // Properties props new Properties(); // props.load(JDBCUtil.class.getClassLoader().getResourceAsStream(db.properties)); // dataSource BasicDataSourceFactory.createDataSource(props); // } catch (Exception e) { // e.printStackTrace(); // } // } // 新Druid方式 private static DruidDataSource dataSource; static { try { Properties props new Properties(); props.load(JDBCUtil.class.getClassLoader().getResourceAsStream(db.properties)); dataSource (DruidDataSource) DruidDataSourceFactory.createDataSource(props); // 開啟SQL監(jiān)控關(guān)鍵 dataSource.setFilters(stat,wall,log4j); } catch (Exception e) { e.printStackTrace(); } }Druid監(jiān)控面板啟用啟動Tomcat后訪問http://localhost:8080/drug/druid/index.html用戶名admin密碼admin可實時查看SQL執(zhí)行列表含耗時、執(zhí)行次數(shù)、參數(shù)綁定值慢SQL執(zhí)行1秒自動標(biāo)紅數(shù)據(jù)源狀態(tài)活躍連接數(shù)、等待線程數(shù)Web應(yīng)用統(tǒng)計URI訪問量、響應(yīng)時間TOP10。后悔藥某次上線后庫存扣減變慢打開Druid發(fā)現(xiàn)UPDATE drug_info SET stock_quantity ? WHERE id ?平均耗時2.3秒。點開詳情發(fā)現(xiàn)drug_info表缺索引——給id字段加主鍵索引后耗時降至15ms。沒有Druid你得靠EXPLAIN一條條查至少浪費半天。5.3 用Filter統(tǒng)一處理中文亂碼告別每個Servlet里寫三行setCharacterEncoding當(dāng)前項目在每個Servlet的doPost()開頭都寫request.setCharacterEncoding(UTF-8); response.setContentType(text/html;charsetUTF-8);這極易遺漏。新建src/com/hospital/drug/filter/EncodingFilter.javapublic class EncodingFilter implements Filter { Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { HttpServletRequest request (HttpServletRequest) req; HttpServletResponse response (HttpServletResponse) resp; // 統(tǒng)一設(shè)置編碼 request.setCharacterEncoding(UTF-8); response.setContentType(text/html;charsetUTF-8); // 放行 chain.doFilter(request, response); } }并在web.xml中注冊filter filter-nameEncodingFilter/filter-name filter-classcom.hospital.drug.filter.EncodingFilter/filter-class /filter filter-mapping filter-nameEncodingFilter/filter-name url-pattern/*/url-pattern !-- 攔截所有請求 -- /filter-mapping效果從此所有Servlet無需再手動設(shè)編碼且Filter在Servlet之前執(zhí)行確保getParameter()拿到的就是UTF-8解碼后的中文。從那以后我每次接手JavaWeb項目第一件事就是加這個Filter——不是為了炫技而是因為漏寫一次setCharacterEncoding就會導(dǎo)致“藥品名稱入庫后變問號”而排查時你得翻遍所有Servlet比寫十行業(yè)務(wù)邏輯還累。希望幫到你。本文還有配套的精品資源點擊獲取