☰
JSP连接Access数据库全攻略:从JDBC-ODBC桥到UCanAccess驱动
2026/10/16 23:25:27 网站建设 项目流程

简介:这是一份JSP连接ACCESS数据库的入门教程资源,面向初学Web开发的读者,重点演示如何通过JDBC-ODBC桥驱动实现JSP与Access数据库的连接及数据读取,适用于小型项目、毕业设计或课程实训场景。资源共1个文件,为doc格式文档,压缩包大小约31KB,内含完整示例代码、配置步骤与运行说明。当前已有240人学习。文档以创建test.mdb数据库、放置到Tomcat指定目录、编写JDBCAccess.jsp三个步骤为主线,详细展开连接字符串、驱动加载、Statement创建、ResultSet结果集遍历以及资源释放等关键环节,并给出可直接套用的JSP页面代码。通过这份文档,读者可以快速搭建一个可运行的JSP+Access数据访问示例,理解JDBC访问数据库的基本流程,同时也能积累有关数据库路径映射、异常处理、字符集设置的实际经验,为后续切换到MySQL、Oracle等主流数据库打下扎实基础。

1. JSP连接ACCESS:小项目里反复被翻出来的老手艺

最近又在网上看到有人问“JSP怎么连ACCESS数据库”。这个组合放在今天确实有点过时,但在课程设计、内部工具、个人博客这类数据量不大的场景里,Access文件型数据库比MySQL省事得多——不用装服务,不用管账号权限,拷一个.mdb就能跑。可真正动手时,光一个JDBC-ODBC桥驱动就能劝退不少人,再加上中文乱码、路径不对、文件锁死这类问题,折腾一下午不算稀奇。这篇笔记就把整套流程拆开讲清楚:从建库放文件,到写连接代码,再到最常见的翻车点,最后给出一条迁移到纯Java驱动的升级路线。新手照步骤做能跑通,熟手也能复用这里面的排查思路。

2. 建库与布署:数据库文件放错位置是第一个翻车点

2.1 建数据库和表:字段类型先定清

Access的数据库是一个文件,整个库的所有表、查询、窗体都装在一个.mdb或.accdb文件里。按教程要求,新建一个test.mdb,在里面建立一个名为username的表,表里有两个字段:uid和pwd,两者都选文本型。

用Access的图形界面做很简单:打开test.mdb,然后选择“使用设计器创建表”,在字段名列输入uid和pwd,数据类型选“短文本”(旧版本叫“文本”)。字段长度默认是255,实际用50就够,密码一般也不会超过50个字符。

如果你习惯用SQL语句,可以在Access的查询视图里执行这一句:

CREATE TABLE username ( uid TEXT(50), pwd TEXT(50) );

注意Access的SQL方言和MySQL不完全一样,这里直接用TEXT指定短文本类型,但有些版本对TEXT的默认长度有争议。更稳妥的方法还是走图形界面,选“短文本”类型,长度自己填50。表结构建好以后,随意插入两三行测试数据,比如:

INSERT INTO username (uid, pwd) VALUES ('admin', '123456'); INSERT INTO username (uid, pwd) VALUES ('guest', 'guest123');

这样后面查询时才有数据可展示。

2.2 数据库文件放哪:Tomcat目录下的隐含规则

示例代码里有一行String spath = "data/test.mdb";,然后通过application.getRealPath(spath)把相对路径转成物理路径。这个方法拿到的基准路径是当前Web应用的根目录,也就是Tomcat下webapps\ROOT这个目录。所以数据库文件必须放在webapps\ROOT\data\下,和WEB-INF目录平级。

有人喜欢把数据库放到D盘某个自定义路径,然后在代码里直接写jdbc:odbc:Driver={Microsoft Access Driver (*.mdb)};DBQ=D:/test.mdb。这种写法在当前机器上能跑,但换一台电脑、换一个Tomcat目录就得改代码,很不方便。更合理的做法是放到Web应用内部,靠getRealPath动态解析。

如果你把文件放在webapps\ROOT\data\test.mdb,那么getRealPath返回的物理路径类似C:\Tomcat\webapps\ROOT\data\test.mdb。注意Windows下的路径反斜杠在连接字符串里需要处理吗?实际上JDBC-ODBC桥能接受反斜杠路径,但为了稳妥,最好先转成正斜杠,比如dbpath = dbpath.replace('\\', '/')。

2.3 getRealPath的坑:它在某些环境下返回null

application.getRealPath()是ServletContext的经典方法,但在现在的Tomcat里,如果你把项目打包成war包并且没有解压目录,这个方法可能返回null。还有在下一次部署时,如果应用以“exploded”方式运行,路径也有可能和预期不一致。

所以拿到dbpath后,不要直接拼进URL,先做一个存在性检查:

String dbpath = application.getRealPath(spath); if (dbpath == null) { throw new RuntimeException("getRealPath返回null,无法定位数据库文件"); } java.io.File dbFile = new java.io.File(dbpath); if (!dbFile.exists()) { throw new RuntimeException("数据库文件不存在: " + dbpath); }

这步检查看似多余,但能省掉后面一大半的排查时间。常见错误是“data”目录建错位置——不是建在工程的根目录下,而是建在了WEB-INF里面,或者干脆忘记建目录。用这个检查就能立刻看到实际报错信息。

2.4 文件权限与锁:Windows下的隐形杀手

Access数据库是文件型数据库,Tomcat运行时使用的系统账户必须有对这个文件及其所在目录的读写权限。如果你把Tomcat注册成Windows服务,默认运行账户是LocalSystem或某个服务账户,对桌面目录、D盘根目录可能没有写权限。一旦连接成功但写入失败,报错信息往往很难看懂。

还有更常见的:你刚用Access程序打开test.mdb看过数据,没有关闭,Tomcat里连接时就会报“文件正在使用”或“数据库独占锁定”。写代码之前,确认Access桌面程序已经彻底退出。这些细节不算技术难点,但十次连接失败里至少有三次是这种环境问题。

3. 连接代码逐段拆解:从Class.forName到ResultSet关闭

3.1 驱动加载与连接URL:为什么是JdbcOdbcDriver

先看一份完整可运行的JSP代码,按示例的结构整理如下:

<%@ page contentType="text/html; charset=gb2312" language="java" import="java.sql.*" %> <% try { // 数据库相对路径 String spath = "data/test.mdb"; // 转成物理路径 String dbpath = application.getRealPath(spath); // 连接字符串,直接通过ODBC驱动访问Access文件 String url = "jdbc:odbc:Driver={Microsoft Access Driver (*.mdb)};DBQ=" + dbpath; // 加载JDBC-ODBC桥驱动 Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); // 建立连接 Connection conn = DriverManager.getConnection(url); // 创建语句对象,可滚动、可更新 Statement stmt = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE); // 查询全部用户 String sql = "select * from username"; ResultSet rs = stmt.executeQuery(sql); while (rs.next()) { out.print("用户名:" + rs.getString("uid")); out.print(" 密码:" + rs.getString("pwd") + "<br>"); } out.print("<br>恭喜你!数据库连接成功!"); // 关闭资源 rs.close(); stmt.close(); conn.close(); } catch (Exception e) { out.print("数据库连接错误!错误信息如下:<br>"); out.print(e.getMessage()); } %>

这段代码的核心是Class.forName("sun.jdbc.odbc.JdbcOdbcDriver")。JDBC-ODBC桥是早期JDK自带的桥接驱动,它的作用是把JDBC调用翻译成ODBC调用,再由ODBC去访问Access。连接URL不是常见的jdbc:mysql://...,而是jdbc:odbc:Driver={Microsoft Access Driver (*.mdb)};DBQ=物理路径。花括号里是ODBC驱动名称,必须是系统里已安装的驱动。

需要说明的是,这里的Driver={Microsoft Access Driver (*.mdb)}并不是在ODBC数据源管理器里配置一个DSN,而是直接指定驱动名,后面用DBQ参数告诉驱动数据库文件在哪里。这种写法省去了手动配置DSN的步骤,方便部署。如果你的Access数据库设置了密码,连接字符串还要加上;PWD=xxxx。示例里用户名和密码都为空,所以没有拼接。

3.2 查询与遍历:ResultSet的基本用法

stmt.executeQuery("select * from username")返回一个ResultSet结果集。ResultSet内部像一个游标,初始位置在第一行之前,rs.next()把游标移到下一行,如果还有数据就返回true,否则返回false。所以while(rs.next())会逐行遍历结果。

rs.getString("uid")表示取出当前行里字段名为uid的值,返回类型是Java字符串。如果你的表字段名是混合大小写,比如Uid,getString是大小写不敏感的,写成uid也能解析。访问完后,注意关闭顺序:先关ResultSet,再关Statement,最后关Connection。如果顺序颠倒,驱动可能无法正常释放资源,Access文件会被一直锁到连接超时。

3.3 资源关闭的改进:用try-with-resources替代手工关闭

手工关闭的问题在于,如果中间抛出异常,后面的关闭代码不会执行。常见的改进是在finally里关闭,但代码会显得啰嗦。Java 7之后可以用try-with-resources,JSP里同样适用:

try (Connection conn = DriverManager.getConnection(url); Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery("select * from username")) { while (rs.next()) { out.print("用户名:" + rs.getString("uid")); out.print(" 密码:" + rs.getString("pwd") + "<br>"); } } catch (Exception e) { out.print("连接出错:" + e.getMessage()); }

这里把Connection、Statement、ResultSet都放在try的括号里,无论正常结束还是抛异常,它们都会自动关闭。这种写法在旧项目里不常见,但完全兼容,建议新写的代码直接采用。

3.4 为什么用Statement而不是PreparedStatement

示例里用的是Statement,直接拼接SQL字符串。如果SQL语句是固定的,不带任何外部参数,用Statement就够了。但一旦查询条件来自用户提交,比如String sql = "select * from username where uid='" + request.getParameter("uid") + "'",就存在SQL注入风险。这种情况下应该用PreparedStatement:

String sql = "select * from username where uid = ?"; PreparedStatement pstmt = conn.prepareStatement(sql); pstmt.setString(1, request.getParameter("uid")); ResultSet rs = pstmt.executeQuery();

PreparedStatement的好处是先把SQL模板交给数据库预编译,参数再通过setString绑定,特殊字符不会被当作SQL语法执行。对于Access这种文件数据库,注入同样危险,不要因为项目小就忽略。

4. 连接Access常见问题与排查:五个实战踩坑记录

4.1 故障一:ClassNotFoundException: sun.jdbc.odbc.JdbcOdbcDriver

现象:JSP页面报错,控制台或浏览器显示java.lang.ClassNotFoundException: sun.jdbc.odbc.JdbcOdbcDriver。

原因:JDK 8开始移除了JDBC-ODBC桥驱动,这个类在标准运行时里已经不存在。如果电脑装的是JDK 8及以上版本,或者Tomcat使用的高版本JRE,Class.forName自然加载不到。

解决:换用纯Java驱动UCanAccess,下一章会详细说明。临时方案是降级到JDK 7,但不建议在生产环境用一个过时的JDK。验证方式可以写一个最简单的Java类测试Class.forName(...)是否能通过,如果抛同样的异常,基本确认是JDK版本问题。

4.2 故障二:数据库路径不对,文件找不到

现象:页面报错信息里能看到java.io.FileNotFoundException: data\test.mdb (系统找不到指定的路径),或者连接URL里DBQ部分是null。

原因:数据库文件没有放在webapps\ROOT\data下;或者getRealPath返回了null;或者路径是反斜杠导致驱动解析异常。

解决:在代码里打印dbpath,并在连接前检查文件是否存在。我一般会在Class.forName之前加一段调试输出:

System.out.println("DB path: " + dbpath); System.out.println("File exists: " + new java.io.File(dbpath).exists());

如果文件确实不存在,就检查data目录是否真的建在Web应用根目录下。很多开发工具把工程根目录和实际部署目录混在一起,在IDE里运行Tomcat时,文件放在src同级目录,部署后却不在webapps下,需要手动把data目录拷贝到部署目录里。

4.3 故障三:数据库文件被独占锁定

现象:页面报错类似Cannot open database,或者The database has been opened by another user。

原因:Access的.mdb文件被桌面版Access程序打开,或者另一个连接没有正常关闭,数据库文件被独占锁住。还有一种情况是Tomcat服务账户没有对文件的写权限。

解决:关闭所有Access窗口,在任务管理器里检查是否有MSACCESS.EXE进程,结束掉。然后确认Tomcat所在目录的文件权限,给Tomcat服务账户至少读写权限。如果是在开发环境,直接用控制台启动Tomcat,通常不会遇到权限问题,但注册成服务后就要格外注意。

4.4 故障四:中文乱码

现象:页面上显示的用户名变成了???,或者写入数据库的中文变成乱码。

原因:JSP页面声明了charset=gb2312,但容器在读取请求参数或输出时用的编码不一致;也可能Access表字段本身存储的编码与页面不一致。

解决:统一页面编码,在JSP头部同时声明contentType和pageEncoding:

<%@ page contentType="text/html; charset=gb2312" pageEncoding="gb2312" %>

如果数据库里已经存入乱码文本,就需要读取后重新转码,比如rs.getString("uid").getBytes("ISO-8859-1"), "gb2312",但这属于补救手段。最省心的做法是库和页面都按GBK或UTF-8统一。Access对UTF-8支持较差,建议页面和数据库都用GBK体系,减少转换中的坑。

4.5 故障五:64位系统下ODBC驱动不一致

现象:在64位Windows上运行,报错提示[Microsoft][ODBC 驱动程序管理器] 未发现数据源名称并且未指定默认驱动程序。

原因:Tomcat以32位模式运行,但系统里装的是64位ODBC驱动,或者反过来。JDBC-ODBC桥访问的是系统ODBC管理器,32位和64位是隔离的,驱动不匹配就会报这个错。

解决:先确认Tomcat的位数,下载与Tomcat相同位数的Microsoft Access Driver,或者在ODBC数据源管理器(区分32位和64位)中选择合适的驱动。这个问题的根源是ODBC的架构限制,与其折腾位数匹配,不如直接换UCanAccess纯Java驱动,从根上绕开ODBC。

5. 升级到纯Java驱动:UCanAccess替换JDBC-ODBC桥

5.1 为什么UCanAccess是当前的最佳选择

JDBC-ODBC桥从JDK 8开始被移除,再用老驱动等于在新环境里强行踩一脚坑。UCanAccess是一个开源的纯Java驱动,直接读取Access数据库文件,不需要任何ODBC配置,跨平台,支持.mdb和.accdb格式。它内部依赖HSQLDB模拟SQL引擎,对常见SQL语句的支持比ODBC桥更好,也支持事务和预处理语句。

UCanAccess由几个jar包组成,核心是ucanaccess,另外需要jackcess、hsqldb、commons-lang3、commons-logging等依赖。如果你用Maven管理项目,引入依赖会自动拉齐。如果是手动部署,要把这些jar包全部放到WEB-INF/lib目录下。没有Maven的情况下,可以直接从UCanAccess的发布页下载压缩包,里面包含了所有依赖。

5.2 改造连接代码:三步切换驱动

第一步:把jar包放到WEB-INF/lib下。第二步:修改驱动类和URL。第三步:调整连接参数。下面是一份改造后的JSP代码:

<%@ page contentType="text/html; charset=gb2312" language="java" import="java.sql.*" %> <% try { String spath = "data/test.mdb"; String dbpath = application.getRealPath(spath); if (dbpath == null || !new java.io.File(dbpath).exists()) { throw new RuntimeException("数据库文件不存在: " + dbpath); } // UCanAccess 驱动类 Class.forName("net.ucanaccess.jdbc.UcanaccessDriver"); // UCanAccess 连接URL,注意前缀是 jdbc:ucanaccess:// String url = "jdbc:ucanaccess://" + dbpath; // 如果数据库设置了密码,追加 user 和 password 参数 // String url = "jdbc:ucanaccess://" + dbpath + ";user=xxx;password=yyy"; Connection conn = DriverManager.getConnection(url); Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery("select * from username"); while (rs.next()) { out.print("用户名:" + rs.getString("uid")); out.print(" 密码:" + rs.getString("pwd") + "<br>"); } out.print("<br>连接成功!"); rs.close(); stmt.close(); conn.close(); } catch (Exception e) { out.print("连接错误:" + e.getMessage()); } %>

对比一下,驱动类从sun.jdbc.odbc.JdbcOdbcDriver变成了net.ucanaccess.jdbc.UcanaccessDriver,URL前缀从jdbc:odbc:变成了jdbc:ucanaccess://,后面直接跟数据库文件的绝对路径。不再有Driver={...}这种ODBC写法,文件路径就是URL的一部分。

5.3 新旧驱动参数对比

对比项JDBC-ODBC桥UCanAccess
驱动类sun.jdbc.odbc.JdbcOdbcDrivernet.ucanaccess.jdbc.UcanaccessDriver
URL前缀jdbc:odbc:Driver={...};DBQ=路径jdbc:ucanaccess://路径
是否依赖ODBC依赖系统ODBC驱动不依赖,纯Java实现
JDK 8+支持不兼容,类已移除兼容
跨平台能力Windows上受限跨平台
数据源配置可在URL中临时指定驱动无需数据源
对Access版本支持主要.mdb.mdb和.accdb
并发性能一般,文件锁明显一般,但比桥接稳定

从表里能看出来,UCanAccess不是性能增强,而是兼容性和可维护性的增强。它让你脱离ODBC这个黑匣子,所有逻辑都在Java层可控。

5.4 性能与并发注意事项

UCanAccess适合单用户或低并发场景,底层用HSQLDB缓存数据库内容,写入时可能把整个数据库重写一遍。如果多个用户同时写入,会出现锁冲突或数据不一致。真到了需要多人并发读写的业务,Access这种文件格局限太大,尽早迁移到MySQL或H2更稳妥。在技术选型时,要清楚自己的边界。

6. 调试技巧:把连接过程变成可观测的日志

6.1 在页面输出关键调试信息

连接失败时,最怕只有一句“数据库连接错误”,完全不知道错在哪一步。建议在开发阶段把关键变量直接输出到页面上:

out.print("spath: " + spath + "<br>"); out.print("dbpath: " + dbpath + "<br>"); out.print("file exists: " + new java.io.File(dbpath).exists() + "<br>");

这段输出能立刻告诉你路径是否正确、文件是否存在。如果是null或false,问题就在布署环节,而不是代码逻辑。确认无误后再注释掉这些输出,或者放在一个开关后面。

6.2 打印完整异常堆栈

示例代码里只打印e.getMessage(),很多关键信息被吞了。比如ClassNotFound的完整堆栈能看出到底缺哪个类。可以改成:

catch (Exception e) { out.print("连接错误:"); e.printStackTrace(new java.io.PrintWriter(out)); }

或者直接把堆栈打到服务器控制台:

} catch (Exception e) { e.printStackTrace(); }

这样Tomcat的logs目录里会留下完整堆栈。我在排查UCanAccess依赖缺失时,就是靠堆栈第一行定位到缺少commons-logging,比猜快得多。

6.3 用try-with-resources再稳一层

前面已经写过改进代码,这里再强调一次:连接代码必须保证Connection关闭,否则再次访问时Access文件会被锁住。尤其是在并发或反复刷新页面时,一个未关闭的连接就可能让整个应用瘫痪。我在实际项目里吃过亏——页面刷新三次后数据库直接锁死,重启Tomcat才恢复。从那以后,我每次写数据库相关代码都强制用try-with-resources,或者至少在finally里关连接。这个习惯救了我很多次。

6.4 写一个独立的Java测试类验证驱动

JSP页面的调试环境太复杂,我一般会先写一个最简Java控制台程序,放到IDE里直接跑,验证驱动、路径、SQL这三件事:

import java.sql.*; public class AccessTest { public static void main(String[] args) throws Exception { Class.forName("net.ucanaccess.jdbc.UcanaccessDriver"); String url = "jdbc:ucanaccess://C:/webapps/ROOT/data/test.mdb"; try (Connection conn = DriverManager.getConnection(url); Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery("select uid, pwd from username")) { while (rs.next()) { System.out.println(rs.getString("uid") + " " + rs.getString("pwd")); } } } }

注意这个测试类里的绝对路径必须和实际部署路径一致。如果这个程序能跑通,而JSP里报错,问题就在ServletContext路径解析或编码上;如果这个程序本身报错,那就先解决驱动依赖或文件路径,再回到JSP。通过这种分层排查,能把环境问题、代码问题、部署问题快速隔离。希望这篇笔记能帮你少走一点弯路,也希望你下次再看到JSP连Access时,心里清楚哪些坑是旧时代留下的,哪些是值得花时间避开的。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询