據(jù)庫:JDBC-ODBC橋接方案詳解)
簡介JSP連接ACCESS數(shù)據(jù)庫是Web開發(fā)中一種基礎(chǔ)的數(shù)據(jù)交互方式常用于小型項(xiàng)目與課堂實(shí)驗(yàn)。教程面向Java Web初學(xué)者幫助讀者快速掌握通過JDBC-ODBC橋驅(qū)動(dòng)訪問Access數(shù)據(jù)庫的完整流程也適合需要完成課程設(shè)計(jì)的在校學(xué)生。內(nèi)容以一個(gè)可運(yùn)行的示例展開先建立包含uid和pwd文本字段的數(shù)據(jù)庫再將數(shù)據(jù)庫文件放入Tomcat的指定data目錄接著編寫JSP頁面完成連接、查詢和結(jié)果展示涵蓋從數(shù)據(jù)庫準(zhǔn)備到頁面輸出的完整鏈路。文檔不僅完整給出可直接運(yùn)行的代碼還逐段解釋連接字符串構(gòu)造、物理路徑轉(zhuǎn)換、驅(qū)動(dòng)加載、資源關(guān)閉等關(guān)鍵環(huán)節(jié)并提示最常見的數(shù)據(jù)庫連接錯(cuò)誤及排錯(cuò)思路方便讀者對(duì)照排查。同時(shí)指出Access更適合學(xué)習(xí)實(shí)踐若遷移到MySQL等企業(yè)級(jí)數(shù)據(jù)庫需更換驅(qū)動(dòng)和連接方式有助于建立更系統(tǒng)的JDBC認(rèn)知。壓縮包內(nèi)僅含1個(gè)doc格式文檔大小約31KB內(nèi)容高度濃縮示例代碼可直接復(fù)制使用。目前已有240人學(xué)習(xí)下載值得正在自學(xué)JSP與數(shù)據(jù)庫交互的讀者參考。1. JSP 連接 ACCESS 數(shù)據(jù)庫一份最輕量的 JDBC-ODBC 橋接樣例JSP 連接 ACCESS 數(shù)據(jù)庫聽起來像十年前的產(chǎn)物但在今天的小型管理系統(tǒng)、課堂演示和內(nèi)部表單場景里它依然是最省事的方案。Access 把數(shù)據(jù)直接存在一個(gè) .mdb 文件里不需要單獨(dú)安裝數(shù)據(jù)庫服務(wù)JSP 頁面通過 JDBC-ODBC 橋讀取文件內(nèi)容整個(gè)項(xiàng)目里只有數(shù)據(jù)庫文件和 JSP 兩樣?xùn)|西。這份資源要做的事很明確創(chuàng)建 test.mdb里面建一張 username 表存 uid、pwd 兩個(gè)文本字段把數(shù)據(jù)庫文件放進(jìn) Tomcat 的 webapps\ROOT\data 目錄再寫一個(gè) JDBCAccess.jsp負(fù)責(zé)加載驅(qū)動(dòng)、建立連接、執(zhí)行 select 查詢并把結(jié)果輸出到瀏覽器。適合剛接觸 Web 開發(fā)的初學(xué)者也適合需要在內(nèi)網(wǎng)機(jī)器上快速復(fù)現(xiàn)老項(xiàng)目的開發(fā)者跑通它等于把 JDBC 和 JSP 內(nèi)置對(duì)象里最常用的一段流程完整走了一遍。2. 環(huán)境準(zhǔn)備Access 建庫、JDK 版本與 Tomcat 目錄三件事這套代碼能跑起來一半靠 JSP 代碼一半靠運(yùn)行環(huán)境。我見過不少人把代碼原樣粘進(jìn)新項(xiàng)目結(jié)果驅(qū)動(dòng)加載就拋異常。問題不在代碼而在 JDK 版本、Access 驅(qū)動(dòng)位數(shù)、數(shù)據(jù)庫文件存放位置這三件事沒對(duì)上。這一章先把環(huán)境鋪好再進(jìn)入代碼拆解。2.1 技術(shù)底座JDBC-ODBC 橋的老原理與新限制JDBC-ODBC 橋的定位是讓 Java 程序通過 JDBC API 訪問任何帶有 ODBC 驅(qū)動(dòng)的關(guān)系型數(shù)據(jù)庫。sun.jdbc.odbc.JdbcOdbcDriver 這個(gè)類把 JDBC 方法調(diào)用轉(zhuǎn)換成 ODBC 調(diào)用再由 ODBC 層加載 Microsoft Access Driver (*.mdb) 去讀寫文件。整個(gè)過程里沒有獨(dú)立的數(shù)據(jù)庫服務(wù)進(jìn)程數(shù)據(jù)文件本身被當(dāng)作數(shù)據(jù)庫來處理。對(duì) Access 這種輕量文件庫來說這個(gè)機(jī)制幾乎是零成本接入。這套橋接機(jī)制在 JDK 7 及以前一直是 JDK 自帶的功能Class.forName(sun.jdbc.odbc.JdbcOdbcDriver) 可以直接加載成功。JDK 8 開始官方移除了這個(gè)橋接類理由是長期無人維護(hù)且官方建議改用廠商提供的原生驅(qū)動(dòng)。因此想原樣跑通這份樣例最省事的是使用 JDK 6 或 JDK 7 配合對(duì)應(yīng)版本的 Tomcat。JSP 頁面里用到的語法都極其基礎(chǔ)不依賴任何新版本特性舊環(huán)境反而更合適。先說一個(gè)實(shí)踐中的認(rèn)知誤區(qū)不是“代碼寫對(duì)了就能連上”。Class.forName 只在 JDK 的類庫里能找到這個(gè)類時(shí)才成功。機(jī)子上裝的是 JDK 8第一行連接就會(huì)拋 ClassNotFoundException。這也是很多人在新環(huán)境復(fù)現(xiàn)老項(xiàng)目時(shí)遇到的第一個(gè)坎。所以拿到這份資源先別急著貼代碼打開命令行執(zhí)行 java -version看版本號(hào)是不是 8 以下。還有一點(diǎn)值得說明為什么不直接用 MySQL 或 PostgreSQL因?yàn)檫@份樣例所在的場景里往往沒有數(shù)據(jù)庫管理員權(quán)限或者目標(biāo)機(jī)器上根本沒裝數(shù)據(jù)庫服務(wù)。Access 文件即庫拷走一個(gè) .mdb 文件就等于遷移了整個(gè)庫這種便攜性在演示、教學(xué)和單機(jī)內(nèi)部工具里非常實(shí)用。它不適合做高并發(fā)在線業(yè)務(wù)這是后續(xù)二次開發(fā)時(shí)要有預(yù)期的地方。2.2 建庫建表test.mdb 與 username 表怎么生成創(chuàng)建數(shù)據(jù)庫文件建議用 Access 桌面版直接操作。如果沒有安裝完整 Office也可以裝 Access Runtime 環(huán)境用空庫入口創(chuàng)建文件。最終需要的產(chǎn)物只有一個(gè)test.mdb 內(nèi)部有一張 username 表表里包含 uid 和 pwd 兩個(gè)文本字段。這一步直接在 Access 的查詢?cè)O(shè)計(jì)視圖里執(zhí)行一句建表 SQL 就能完成。CREATE TABLE username ( uid TEXT(50), pwd TEXT(50) );這段 SQL 建的表沒有主鍵學(xué)習(xí)階段足夠用。字段大小寫成 TEXT(50)是因?yàn)榇a里的 rs.getString(uid) 本來就是按字符串讀取50 的容量存放普通用戶名和密碼不會(huì)超長。生產(chǎn)級(jí)設(shè)計(jì)里通常還會(huì)給 uid 加唯一約束但本題場景保持原樣更貼近樣例本身。有一點(diǎn)需要提醒Access 的 SQL 方言和 MySQL 有不小差別它不支持在同一個(gè)建表語句里寫 IF NOT EXISTS。重復(fù)執(zhí)行上面的 SQL 會(huì)提示“數(shù)據(jù)庫中已存在同名對(duì)象”。因此建表前先確認(rèn)庫是新創(chuàng)建的或者手動(dòng)刪掉舊表再重新建。建完表之后順手往表里插入兩條數(shù)據(jù)讓 JSP 查詢時(shí)有內(nèi)容可顯示。這步同樣在 Access 查詢視圖中執(zhí)行。INSERT INTO username (uid, pwd) VALUES (admin, 123456); INSERT INTO username (uid, pwd) VALUES (guest, guest);插入完成后可以打開表確認(rèn)數(shù)據(jù)存在然后保存文件。注意保存時(shí)不要勾選需要登錄才能打開庫的選項(xiàng)否則 JSP 連接時(shí)會(huì)被攔在數(shù)據(jù)庫認(rèn)證這一步。2.3 數(shù)據(jù)庫放哪getRealPath 的路徑映射機(jī)制數(shù)據(jù)庫文件做出來之后第二個(gè)關(guān)鍵步驟是放對(duì)位置。按這份樣例的約定test.mdb 放在 Tomcat 的 webapps\ROOT\data 目錄下。這里要特別注意ROOT 不是磁盤根目錄而是 Tomcat 中默認(rèn) Web 應(yīng)用上下文對(duì)應(yīng)的目錄也就是打開 http://localhost:8080 時(shí)訪問的那個(gè)應(yīng)用根目錄。代碼里使用 application.getRealPath(spath) 來獲取物理路徑。application 是 JSP 內(nèi)置的 ServletContext 對(duì)象getRealPath 接收一個(gè)相對(duì)于當(dāng)前 Web 應(yīng)用根目錄的虛擬路徑返回對(duì)應(yīng)的磁盤絕對(duì)路徑。舉個(gè)例子Tomcat 安裝在 C 盤默認(rèn)目錄那么 getRealPath(data/test.mdb) 一般會(huì)得到類似 C:\Tomcat\webapps\ROOT\data\test.mdb 的結(jié)果。這個(gè)機(jī)制的價(jià)值在于你不需要在代碼里寫死機(jī)器路徑部署到任何目錄都能自動(dòng)推導(dǎo)出數(shù)據(jù)庫的真實(shí)位置。很多初學(xué)者習(xí)慣直接寫 D:\test.mdb 這種絕對(duì)路徑結(jié)果換一臺(tái)機(jī)器就報(bào)文件找不到。改用 getRealPath 之后只要 .mdb 文件在 JSP 所在的同一個(gè)應(yīng)用目錄里路徑推導(dǎo)就永遠(yuǎn)正確。這也解釋了為什么必須在 ROOT 下建 data 目錄——因?yàn)?JSP 部署在 ROOT 里相對(duì)路徑以 ROOT 為基準(zhǔn)。整個(gè)應(yīng)用目錄的最終形態(tài)如下webapps └── ROOT ├── JDBCAccess.jsp └── data └── test.mdb這里有一個(gè) Windows 下容易被忽略的細(xì)節(jié)如果 Tomcat 被安裝到 Program Files 這類需要管理員權(quán)限的目錄下JSP 讀取數(shù)據(jù)庫一般沒問題但后續(xù)要執(zhí)行 insert 時(shí)可能會(huì)因?yàn)閷憴?quán)限不足而報(bào)“文件共享鎖定數(shù)不夠”。遇到時(shí)優(yōu)先把 Tomcat 換到純英文且無空格、無系統(tǒng)保護(hù)的目錄里能省下不少折騰時(shí)間。3. 核心代碼逐段拆解一個(gè) JSP 頁面走完連接到輸出的完整鏈路樣例里最核心的就是 JDBCAccess.jsp。它把完整鏈路切成四段加載驅(qū)動(dòng)、建立連接、執(zhí)行查詢、輸出結(jié)果。每一段都有對(duì)應(yīng)代碼下面把每一段的含義和參數(shù)說明展開講。3.1 連接段url 拼裝、驅(qū)動(dòng)加載與建立連接整個(gè) JSP 的入口先聲明頁面類型和導(dǎo)入 Java SQL 包然后是連接代碼。下面是最核心的連接片段。try { String spath data/test.mdb; // 相對(duì) Web 應(yīng)用根目錄的路徑 String dbpath application.getRealPath(spath); // 將相對(duì)路徑轉(zhuǎn)成磁盤絕對(duì)路徑 String url jdbc:odbc:Driver{Microsoft Access Driver (*.mdb)};DBQ dbpath; Class.forName(sun.jdbc.odbc.JdbcOdbcDriver); // 加載 JDBC-ODBC 橋驅(qū)動(dòng) Connection conn DriverManager.getConnection(url); // 建立數(shù)據(jù)庫連接第一行的 spath 是相對(duì)于應(yīng)用根目錄的路徑正斜杠在 Windows 下同樣兼容。第三行把數(shù)據(jù)源名 dbname、用戶名 user、密碼 password 都留成空字符串因?yàn)?Access 文件型數(shù)據(jù)庫默認(rèn)不需要賬號(hào)體系。真正的連接字符串由固定前綴和動(dòng)態(tài) DBQ 參數(shù)拼接而成。url 的值最終類似于 jdbc:odbc:Driver{Microsoft Access Driver (*.mdb)};DBQC:\Tomcat\webapps\ROOT\data\test.mdb。這里的每一段都不能寫錯(cuò)。Driver 后面的花括號(hào)是 ODBC 驅(qū)動(dòng)名的一部分刪掉花括號(hào) ODBC 管理器就識(shí)別不了驅(qū)動(dòng)DBQ 是 Database Qualified 的縮寫后面直接跟數(shù)據(jù)庫文件的物理路徑分號(hào)是參數(shù)分隔符路徑里如果出現(xiàn)分號(hào)或者特殊字符就會(huì)引發(fā)解析問題。url 拼好后Class.forName 觸發(fā)驅(qū)動(dòng)的靜態(tài)注冊(cè)邏輯把 JdbcOdbcDriver 注冊(cè)到 DriverManager 的驅(qū)動(dòng)列表里隨后 DriverManager.getConnection(url) 根據(jù) url 前綴找到 ODBC 橋驅(qū)動(dòng)完成連接。很多人在這一步容易踩到幾個(gè)理解偏差。一是認(rèn)為 Class.forName 返回的對(duì)象需要用變量接住其實(shí)這一步只為了觸發(fā)類加載和注冊(cè)返回值完全可以忽略。二是認(rèn)為 DriverManager.getConnection 必須傳用戶名密碼實(shí)際 Access 場景里直接傳一個(gè) url 參數(shù)就夠了用戶名密碼留空即可。三是認(rèn)為 url 里的 DBQ 可以寫相對(duì)路徑不行ODBC 驅(qū)動(dòng)只認(rèn)磁盤絕對(duì)路徑這也是前面必須用 getRealPath 的原因。3.2 查詢段Statement 的滾動(dòng)參數(shù)與 ResultSet 取值連接建立后就是執(zhí)行 SQL 查詢并讀取結(jié)果集。這一段是整份樣例的輸出核心。Statement stmt conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE); ResultSet rs stmt.executeQuery(select * from username); while (rs.next()) { out.print(用戶名: rs.getString(uid)); out.print( 密碼: rs.getString(pwd) br); } out.print(br恭喜你數(shù)據(jù)庫連接成功);conn.createStatement 接收兩個(gè)常量參數(shù)ResultSet.TYPE_SCROLL_SENSITIVE 表示結(jié)果集可以雙向滾動(dòng)并且能感知到其他連接的更新ResultSet.CONCUR_UPDATABLE 表示允許通過結(jié)果集直接更新數(shù)據(jù)庫。對(duì)于學(xué)習(xí)這個(gè)樣例而言這兩個(gè)參數(shù)可以用默認(rèn)的 createStatement() 簡寫替代效果一致。這里特意寫成完整參數(shù)是想展示 JDBC 結(jié)果集控制的完整能力。executeQuery 執(zhí)行 select 語句并返回 ResultSet 對(duì)象。rs.next() 的作用是讓游標(biāo)向后移動(dòng)一行返回 true 時(shí)表示當(dāng)前行有數(shù)據(jù)返回 false 表示結(jié)果集已經(jīng)讀完。rs.getString(uid) 按列名從當(dāng)前行取出 uid 字段的值也可以按列索引寫成 rs.getString(1)。頁面上的 out.print 是 JSP 內(nèi)置對(duì)象 response 的簡化輸出方式最終輸出到瀏覽器響應(yīng)體里。這里有一個(gè)初學(xué)者很容易誤會(huì)的現(xiàn)象如果 username 表是空表while 循環(huán)一次都不會(huì)進(jìn)入頁面只會(huì)顯示最后的“恭喜你”一行。新手往往以為代碼沒生效實(shí)際上是因?yàn)楸砝餂]有數(shù)據(jù)。所以建庫時(shí)往里插入兩行測試數(shù)據(jù)頁面一打開就能看到明確效果。關(guān)于關(guān)閉順序代碼里先 rs.close()再 stmt.close()最后 conn.close()三個(gè)關(guān)閉操作嚴(yán)格遵循由內(nèi)到外的順序。ResultSet 是建立在 Statement 之上的Statement 又依賴于 Connection反向關(guān)閉會(huì)導(dǎo)致底層資源釋放不完整在高頻訪問時(shí)容易把數(shù)據(jù)庫連接耗盡。雖然 JSP 頁面結(jié)束時(shí)會(huì)自動(dòng)清理本次請(qǐng)求的資源但養(yǎng)成手動(dòng)關(guān)閉的習(xí)慣在寫真正業(yè)務(wù)代碼時(shí)非常重要。3.3 輸出與異常錯(cuò)誤信息如何暴露在頁面上整段代碼被一個(gè) try-catch 包裹一旦任意一個(gè)環(huán)節(jié)出現(xiàn)異常catch 塊會(huì)把異常信息輸出到頁面上。} catch (Exception e) { out.print(數(shù)據(jù)庫連接錯(cuò)誤,錯(cuò)誤信息如下br); out.print(e.getMessage()); }catch 塊里調(diào)用 e.getMessage() 而不是 e.printStackTrace()這是一個(gè)面向頁面調(diào)試的取舍。getMessage() 返回的是異常的一句話描述適合直接顯示給瀏覽器printStackTrace() 會(huì)把完整堆棧打到 Tomcat 控制臺(tái)信息更全但對(duì)頁面調(diào)試不友好。建議排查時(shí)兩者配合先用 getMessage() 看清楚錯(cuò)誤類型再翻控制臺(tái)看具體棧幀。頁面頂部的 contentTypetext/html; charsetgb2312 和 meta 標(biāo)簽聲明了輸出編碼。gb2312 對(duì)簡體中文夠用但現(xiàn)代 Tomcat 默認(rèn)以 UTF-8 解析 JSP 文件如果文件保存編碼和頁面聲明不一致就會(huì)出現(xiàn)中文亂碼。這個(gè)問題會(huì)在后面的避坑章節(jié)專門展開。4. 避坑指南驅(qū)動(dòng)消失、路徑空格、亂碼和文件鎖的排查經(jīng)驗(yàn)下面把這份樣例最常見的問題按“現(xiàn)象 → 原因 → 解決”逐條整理。這些都是實(shí)際部署時(shí)反復(fù)出現(xiàn)的照著排查能省下大半天的定位時(shí)間?,F(xiàn)象一頁面報(bào) ClassNotFoundException: sun.jdbc.odbc.JdbcOdbcDriver。原因是 JDK 8 及以上已經(jīng)移除了 JDBC-ODBC 橋驅(qū)動(dòng)類不存在于類庫中Tomcat 運(yùn)行時(shí)使用的是高版本 JDKClass.forName 自然找不到。解決方法是安裝 JDK 7 并把 Tomcat 的 JAVA_HOME 變量指向它保持原代碼不修改如果必須用新 JDK可以改用 UCanAccess 這類純 Java 驅(qū)動(dòng)把連接字符串換成 jdbc:access:// 協(xié)議但這就偏離了樣例本身的橋接方式?,F(xiàn)象二驅(qū)動(dòng)加載成功但連接失敗提示“未發(fā)現(xiàn)數(shù)據(jù)源名稱并且未指定默認(rèn)驅(qū)動(dòng)程序”。常見原因是 64 位系統(tǒng)下的 ODBC 驅(qū)動(dòng)與 JDK 位數(shù)不匹配32 位 JDK 找不到 64 位的 Access 驅(qū)動(dòng)還有一種可能是機(jī)器上安裝的 Access 驅(qū)動(dòng)顯示名不是花括號(hào)里的寫法。解決方法是打開 ODBC 數(shù)據(jù)源管理器32 位系統(tǒng)運(yùn)行 odbcad32.exe64 位系統(tǒng)要區(qū)分 SysWOW64 下的版本在“驅(qū)動(dòng)程序”選項(xiàng)卡里核對(duì)驅(qū)動(dòng)顯示名并安裝與 JDK 位數(shù)一致的 Microsoft Access Database Engine?,F(xiàn)象三頁面提示找不到文件或路徑錯(cuò)誤但 data 目錄和 test.mdb 確實(shí)存在。最常見的原因是 Tomcat 安裝路徑帶有空格比如 Windows 默認(rèn)的 Program Files 目錄DBQ 參數(shù)拼接后無法正確解析。解決方法是把 Tomcat 挪到純英文且無空格的目錄如 D:\Tomcat或者臨時(shí)在代碼里輸出 dbpath 變量確認(rèn) getRealPath 到底解析到了哪個(gè)物理路徑。注意絕對(duì)路徑方案雖然能繞過 getRealPath 的問題但換機(jī)器后必須改代碼只適合應(yīng)急。現(xiàn)象四頁面中文全部變成問號(hào)或亂碼。原因是 JSP 文件聲明的是 gb2312但文件實(shí)際以 UTF-8 保存Tomcat 解析 JSP 時(shí)按文件原始編碼讀取字符流最終與頁面聲明的輸出編碼不一致。解決方法是把頁面的 contentType 和 meta 標(biāo)簽統(tǒng)一改成 UTF-8同時(shí)確保 JSP 文件用 UTF-8 編碼保存。數(shù)據(jù)庫里已經(jīng)存在的亂碼數(shù)據(jù)需要重新錄入不是改頁面編碼就能恢復(fù)的。現(xiàn)象五能連接成功但執(zhí)行 insert 時(shí)報(bào)“文件共享鎖定數(shù)不夠”或“數(shù)據(jù)庫被鎖定”。原因是 Access 文件鎖機(jī)制要求同一時(shí)刻只能有一個(gè)進(jìn)程以讀寫方式打開數(shù)據(jù)庫Tomcat 進(jìn)程和本機(jī)手工打開的 Access 客戶端之間發(fā)生競爭殘留的 .ldb 鎖文件也會(huì)導(dǎo)致異常。解決方法是排查是否有人用 Access 打開了 test.mdb關(guān)閉所有外部進(jìn)程刪除 data 目錄下的 .ldb 文件再給目錄授權(quán)寫權(quán)限。修改數(shù)據(jù)庫結(jié)構(gòu)時(shí)先停 Tomcat 再操作改完再啟動(dòng)是比較穩(wěn)妥的流程。5. 運(yùn)行驗(yàn)證部署進(jìn) Tomcat 后如何確認(rèn)真正讀到了庫代碼和環(huán)境都準(zhǔn)備好之后剩下的問題就是怎么把項(xiàng)目跑起來并且在出錯(cuò)時(shí)定位到具體環(huán)節(jié)。這一章給出完整的部署路徑和驗(yàn)收方法。5.1 目錄復(fù)制與啟動(dòng)順序部署前先確認(rèn)最終目錄結(jié)構(gòu)只包含兩個(gè)必要文件。以 Tomcat 6.0 為例把 JDBCAccess.jsp 復(fù)制到 webapps\ROOT 目錄下把 test.mdb 復(fù)制到 webapps\ROOT\data 目錄下。如果 ROOT 下還沒有 data 目錄手動(dòng)新建一個(gè)Tomcat 不會(huì)自動(dòng)創(chuàng)建它。目錄確認(rèn)無誤后進(jìn)入 Tomcat 的 bin 目錄雙擊 startup.batWindows或執(zhí)行 ./startup.shLinux。看到控制臺(tái)輸出類似“Server startup in xxx ms”的信息后打開瀏覽器訪問 http://localhost:8080/JDBCAccess.jsp。如果 8080 端口被占用需要先修改 conf\server.xml 里的 Connector port再重啟 Tomcat。這里有一個(gè)部署路徑的細(xì)節(jié)如果不想把文件放在 ROOT 里也可以在 webapps 下新建一個(gè)名為 app 的目錄把 JSP 和 data 目錄都放進(jìn)去然后訪問 http://localhost:8080/app/JDBCAccess.jsp。這種情況下 application.getRealPath 的相對(duì)基準(zhǔn)會(huì)跟著應(yīng)用上下文變化getRealPath(data/test.mdb) 會(huì)解析到 webapps\app\data\test.mdb不需要改代碼。這也體現(xiàn)了 getRealPath 相對(duì)路徑設(shè)計(jì)的好處應(yīng)用目錄遷移不會(huì)破壞路徑映射。5.2 頁面結(jié)果與錯(cuò)誤信息對(duì)照正常情況下頁面會(huì)依次輸出 username 表里的所有記錄最后一行是“恭喜你數(shù)據(jù)庫連接成功”。如果數(shù)據(jù)庫里插入了前面準(zhǔn)備的兩條測試數(shù)據(jù)頁面應(yīng)該顯示用戶名:admin 密碼:123456 用戶名:guest 密碼:guest 恭喜你數(shù)據(jù)庫連接成功如果頁面出現(xiàn)異常信息先別急著改代碼對(duì)照異常內(nèi)容判斷屬于哪一層。下面這張表是實(shí)際運(yùn)行中最常出現(xiàn)的錯(cuò)誤信息與處理方向錯(cuò)誤信息片段問題層處理方向ClassNotFoundException: JdbcOdbcDriverJDK 層換回 JDK 7或改用第三方 Access 驅(qū)動(dòng)未發(fā)現(xiàn)數(shù)據(jù)源名稱并且未指定默認(rèn)驅(qū)動(dòng)程序ODBC 層核對(duì)驅(qū)動(dòng)顯示名匹配 JDK 位數(shù)文件名或路徑錯(cuò)誤 / 數(shù)據(jù)庫名被截?cái)嗦窂綄訖z查 DBQ 路徑移除空格和中文java.io.FileNotFoundException路徑層檢查 data 目錄與 test.mdb 文件名是否完全一致文件共享鎖定數(shù)不夠文件鎖層關(guān)閉外部 Access 進(jìn)程刪除 .ldb 文件5.3 用 dbpath 做現(xiàn)場診斷頁面報(bào)錯(cuò)但看不清是路徑問題還是驅(qū)動(dòng)問題時(shí)可以直接在代碼里臨時(shí)加一行輸出。out.print(DBPath dbpath br);這行放在 url 拼裝之前刷新頁面就能看到 JSP 實(shí)際解析到的磁盤絕對(duì)路徑。把這個(gè)路徑復(fù)制到資源管理器里檢查能立刻確認(rèn)文件是否真的存在于該目錄、文件名大小寫是否一致、路徑中是否包含空格或中文。確認(rèn)完之后把這一行刪掉避免正式頁面暴露服務(wù)器路徑。這個(gè)診斷手段也適合排查“代碼在別人電腦上能跑在我這里不行”的問題。大部分情況下最終都會(huì)落在 dbpath 解析結(jié)果和本機(jī) Access 驅(qū)動(dòng)位數(shù)這兩個(gè)變量上。把輸出結(jié)果對(duì)比一下問題往往馬上定位。6. 二次開發(fā)把只讀樣例改成能登錄、能寫入的工具6.1 從查詢改成登錄校驗(yàn)最常見的改造方向是把頁面變成登錄頁。在 JSP 里接收表單提交的 uid 和 pwd拼到查詢條件里有結(jié)果就提示登錄成功沒有結(jié)果就提示失敗。String uid request.getParameter(uid); String pwd request.getParameter(pwd); String sql select * from username where uid uid and pwd pwd ;這個(gè)寫法能快速演示業(yè)務(wù)邏輯但存在 SQL 拼接注入風(fēng)險(xiǎn)。做二次開發(fā)時(shí)第一件事應(yīng)該把 Statement 換成 PreparedStatement用占位符傳參。6.2 把連接代碼抽成公共方法每個(gè) JSP 都重復(fù)寫一遍 Class.forName 和 DriverManager.getConnection代碼會(huì)越來越臃腫。常見做法是寫一個(gè) db.Conn 工具類把驅(qū)動(dòng)加載和連接創(chuàng)建都封裝進(jìn)去頁面里只調(diào)用一句話。連接參數(shù)放到一個(gè)配置變量里統(tǒng)一管理后續(xù)換數(shù)據(jù)庫或調(diào)整路徑只需要改一處。6.3 擴(kuò)展成增刪改把 executeQuery 換成 executeUpdate就能執(zhí)行 insert、update、delete 語句。Access 的 SQL 語法與標(biāo)準(zhǔn) SQL 基本一致插入數(shù)據(jù)用 INSERT INTO 表名 (字段) VALUES (值)。需要注意 Access 的并發(fā)能力弱這個(gè)方案更適合后臺(tái)管理和教學(xué)演示不要把它推到高并發(fā)的線上業(yè)務(wù)里。我在剛學(xué) JSP 時(shí)也是從這份橋接代碼起步的。那之后每次在陌生機(jī)器上跑老項(xiàng)目我都會(huì)先敲一遍 java -version 確認(rèn) JDK 版本再從 ODBC 管理器核對(duì)驅(qū)動(dòng)位數(shù)最后才動(dòng)代碼。這套習(xí)慣幫我避開了無數(shù)次驅(qū)動(dòng)消失和路徑解析的坑。希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取