
简介这份资源面向JSP入门学习者与小型Web项目开发者聚焦JSP通过JDBC-ODBC桥连接ACCESS数据库并读取数据的基础实践帮助读者理解Java Web与数据库交互的完整链路。包内为1个doc文档压缩包约31KB以图文与代码示例形式呈现便于边看边在本地环境复现。文档围绕建立test.mdb数据库、设计username表及uid、pwd文本字段、将数据库文件放入Tomcat指定目录、编写JDBCAccess.jsp等环节展开并给出加载sun.jdbc.odbc.JdbcOdbcDriver、建立连接、执行查询、遍历结果集及关闭资源的完整代码与注释说明同时提示连接字符串与路径需按实际环境调整。已有241人学习适合作为JDBC入门的对照范例与排错参考。1. 一个 JSP 页面连 Access老项目里最容易被低估的“最后一公里”接手一个十年前的教学管理系统时最让人头疼的往往不是业务逻辑而是首页那个“数据库连接失败”的红字提示。项目用的是 JSP 连接 Access 数据库test.mdb就躺在 Tomcat 的webapps/ROOT/data目录下代码看起来只有几十行但换一台机器就报java.sql.SQLException。这类 JSP 连 Access 的场景在小型项目、课程设计、实验室内部工具里非常常见数据量不大、部署简单、不需要独立数据库服务一个.mdb文件加一个 Tomcat 就能跑。适合谁适合手里有老 JSP 工程要维护、或者想理解 JDBC-ODBC 桥接到底怎么把 Web 容器和桌面数据库串起来的开发者。下面从目录结构、连接代码、驱动加载到踩坑排查把这条链路完整拆一遍。2. 项目结构与运行环境文件放哪、容器怎么起2.1 目录布局与文件职责这个项目的物理结构非常扁平核心只有三样东西Access 数据库文件、JSP 页面、Tomcat 容器。把test.mdb放在webapps/ROOT/data下不是随便选的因为 JSP 里用的是application.getRealPath(data/test.mdb)这个 API 返回的是当前 Web 应用根目录下的物理路径。ROOT 是 Tomcat 默认的根应用所以getRealPath拼出来的就是.../webapps/ROOT/data/test.mdb。如果换成自定义应用名比如myapp那文件就要放到webapps/myapp/data下否则路径对不上。典型的部署目录长这样Tomcat 6.0/ ├── webapps/ │ └── ROOT/ │ ├── data/ │ │ └── test.mdb # Access 数据库文件 │ ├── JDBCAccess.jsp # 数据访问测试页 │ └── WEB-INF/ │ └── web.xml ├── lib/ │ └── (JDK 自带的 rt.jar 内含 JDBC-ODBC 桥) └── conf/ └── server.xml这里有个容易被忽略的点test.mdb不需要打进 WAR 包也不需要放在WEB-INF下。放在ROOT/data的好处是路径直观、getRealPath能直接命中坏处是文件暴露在 Web 根目录下理论上可以被直接下载。生产环境常见做法是把 mdb 放到容器外的固定目录JSP 里写绝对路径或通过配置读取避免数据库文件被 HTTP 直接访问。2.2 运行环境与依赖版本这套方案对版本比较敏感不是随便一个 Tomcat 加 JDK 就能跑。JDBC-ODBC 桥驱动sun.jdbc.odbc.JdbcOdbcDriver是 JDK 自带的从 JDK 1.1 一直带到 JDK 7JDK 8 开始被标记为废弃但还能用JDK 11 之后彻底移除。所以如果拿 JDK 17 去跑这段代码Class.forName会直接抛ClassNotFoundException这不是代码写错了是驱动没了。组件可用版本范围说明JDK1.6 ~ 1.8JDK 8 仍含桥驱动JDK 11 移除Tomcat6.x / 7.x / 8.x示例路径按 Tomcat 6 写7/8 结构一致Access 驱动Microsoft Access Driver (*.mdb)32 位 Office 自带64 位系统需注意位数匹配数据库文件.mdbAccess 2003 及以前.accdb 需要换驱动名位数匹配是血泪经验里排第一的坑Tomcat 跑在 64 位 JVM 上但系统里装的 ODBC 驱动是 32 位的连接时就会报“未找到驱动程序”。因为 JDBC-ODBC 桥最终调用的是操作系统的 ODBC 管理器JVM 位数和 ODBC 驱动位数必须一致。常见做法是确认控制面板 → 管理工具 → 数据源(ODBC)里能看到Microsoft Access Driver (*.mdb)64 位系统要专门看 64 位 ODBC 管理器那一项。2.3 启动与验证步骤把文件放好后启动 Tomcat访问http://localhost:8080/JDBCAccess.jsp。如果页面输出“用户名: xxx 密码: xxx”和“恭喜你数据库连接成功”说明整条链路通了。如果只看到异常信息先别改代码按顺序确认三件事mdb 文件是否真的在getRealPath指向的位置、JVM 位数和 ODBC 驱动位数是否一致、username表名和字段名是否和 SQL 里写的一致。这三步能解决八成以上的连接失败。3. 核心连接代码拆解从 getRealPath 到 ResultSet 的完整链路3.1 路径转换为什么不能直接写相对路径JSP 里第一行关键代码是application.getRealPath(spath)。很多人会问既然 mdb 就在data/test.mdb为什么不能直接把data/test.mdb塞进连接字符串因为 JDBC-ODBC 桥最终要把路径交给操作系统的 ODBC 驱动驱动需要的是文件系统绝对路径而不是 Web 应用的相对路径。getRealPath的作用就是把 Servlet 上下文里的相对路径翻译成服务器磁盘上的真实路径。% String spath data/test.mdb; // Web 应用内的相对路径 String dbpath application.getRealPath(spath); // 转成磁盘绝对路径 // 典型输出C:\...\webapps\ROOT\data\test.mdb %参数说明spath必须以/或相对应用根目录的形式书写不能带盘符application是 JSP 内置的ServletContext对象getRealPath在应用未解压比如直接跑 WAR时可能返回 null这是另一个隐蔽的坑。如果返回 null说明容器没有把应用展开到磁盘需要配置 Tomcat 解压 WAR 或者改用绝对路径。3.2 连接字符串与驱动加载连接字符串的格式是jdbc:odbc:Driver{驱动名};DBQ数据库绝对路径。这里的Driver{Microsoft Access Driver (*.mdb)}必须和 ODBC 管理器里显示的驱动名一字不差包括括号和空格。驱动名写错报的是“找不到驱动程序”而不是“文件不存在”排查时容易误判。% String url jdbc:odbc:Driver{Microsoft Access Driver (*.mdb)};DBQ dbpath; Class.forName(sun.jdbc.odbc.JdbcOdbcDriver); // 加载 JDBC-ODBC 桥 Connection conn DriverManager.getConnection(url); %Class.forName这一行的作用是触发驱动类的静态初始化块把驱动注册到DriverManager。JDBC 4.0 之后支持 SPI 自动发现理论上可以省略但 JDBC-ODBC 桥属于老式驱动显式加载更稳妥。DriverManager.getConnection(url)只传了一个参数因为 Access 数据库通常没有用户名密码如果 mdb 设置了数据库密码需要写成getConnection(url, user, password)但注意这是数据库级密码不是文件打开密码两者不是一回事。3.3 Statement 与 ResultSet 的参数含义创建 Statement 时用了两个参数ResultSet.TYPE_SCROLL_SENSITIVE和ResultSet.CONCUR_UPDATABLE。前者表示结果集可滚动且对底层数据变化敏感后者表示结果集可更新。对于只是select展示的场景这两个参数其实不是必需的用无参的createStatement()也能跑。但原代码这么写说明作者可能预留了翻页或就地更新的意图。% 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); } %需要提醒的是JDBC-ODBC 桥对CONCUR_UPDATABLE的支持并不完整某些驱动组合下会降级为只读甚至抛异常。如果只是读取展示我一般会改成createStatement()少一层兼容性风险。rs.getString(uid)按列名取值列名大小写不敏感但表里字段名如果是UID而 SQL 写uid在 Access 里通常没问题换到其他数据库就要注意。3.4 资源关闭与异常处理原代码在try块末尾依次rs.close()、stmt.close()、conn.close()顺序是对的先关结果集再关语句最后关连接。但问题在于如果executeQuery抛异常这三个close都不会执行连接就泄漏了。Tomcat 连接池没配的情况下泄漏几次之后应用就会卡死。% Connection conn null; Statement stmt null; ResultSet rs null; try { // ... 连接与查询 } catch (Exception e) { out.print(数据库连接错误 e.getMessage()); } finally { // 常见做法在 finally 里按反序关闭每个 close 单独 try-catch try { if (rs ! null) rs.close(); } catch (Exception ignored) {} try { if (stmt ! null) stmt.close(); } catch (Exception ignored) {} try { if (conn ! null) conn.close(); } catch (Exception ignored) {} } %这是这个项目最值得改造的地方之一。原代码把关闭放在 try 末尾属于“顺利路径才关闭”一旦出错就漏。改成finally块之后无论查询成功还是失败资源都能释放。每个close单独包 try-catch 是因为关闭本身也可能抛异常不能因为关结果集失败就不关连接。4. 避坑与排查连接失败时先看这五条4.1 报“找不到驱动程序”但驱动明明装了现象页面输出java.sql.SQLException: [Microsoft][ODBC 驱动程序管理器] 未找到数据源名称并且未指定默认驱动程序。原因通常不是驱动没装而是 JVM 位数和 ODBC 驱动位数不匹配。64 位 Tomcat 调的是 64 位 ODBC 管理器而很多 Office 安装的是 32 位驱动。解决在C:\Windows\SysWOW64\odbcad32.exe32 位和C:\Windows\System32\odbcad32.exe64 位里分别确认驱动是否存在让 Tomcat 的位数和驱动位数对齐或者换用 UCanAccess 这类纯 Java 驱动。4.2 getRealPath 返回 null现象连接字符串里的DBQ后面是空的报“文件名无效”。原因应用以 WAR 包形式部署且容器未解压getRealPath拿不到磁盘路径。解决在 Tomcat 的server.xml里确认unpackWARstrue或者把 mdb 放到容器外固定目录JSP 里直接写绝对路径绕开getRealPath。4.3 表名或字段名对不上现象连接成功但查询报“找不到表”或“找不到列”。原因Access 里建的表叫username但字段可能被建成了u_id、password或者表名带了空格。解决打开 mdb 确认表结构和字段拼写SQL 里的列名和rs.getString里的列名必须与表定义一致。Access 对中文表名和字段名也支持但编码不一致时容易出乱码建议统一用英文。4.4 中文乱码现象页面显示的用户名是问号或乱码。原因JSP 页面声明了charsetgb2312但 Access 数据库里存的是另一种编码或者 Tomcat 的 URI 编码配置不一致。解决先确认 mdb 里数据本身是否正常再检查 JSP 的pageEncoding和contentType是否统一。老项目里 gb2312 和 UTF-8 混用是常态常见做法是页面统一改 UTF-8数据库连接不做额外转码让 JDBC-ODBC 桥按系统默认编码处理。4.5 JDK 升级后代码直接崩现象换到 JDK 11 或更高版本Class.forName(sun.jdbc.odbc.JdbcOdbcDriver)抛ClassNotFoundException。原因JDBC-ODBC 桥在 JDK 11 中被移除。解决要么把 JDK 降回 8要么换驱动。换驱动的话UCanAccess 是纯 Java 方案不需要 ODBC依赖几个 jar 包连接串改成jdbc:ucanaccess://路径代码结构基本不用大改。这是老项目迁移时最实际的一条路。5. 二次开发改造把演示代码变成能维护的数据访问层原代码把连接、查询、输出全塞在一个 JSP 里作为教学演示没问题但稍微加两个页面就会重复三遍连接逻辑。二次开发的第一步不是加功能而是把数据库访问抽出来。我一般会建一个简单的工具类把连接和关闭封装掉JSP 只负责取数据和渲染。// DBUtil.java —— 放在 WEB-INF/classes 下或打成 jar import java.sql.*; public class DBUtil { // 从 ServletContext 传入的绝对路径构造连接 public static Connection getConnection(String dbAbsolutePath) throws Exception { String url jdbc:odbc:Driver{Microsoft Access Driver (*.mdb)};DBQ dbAbsolutePath; Class.forName(sun.jdbc.odbc.JdbcOdbcDriver); return DriverManager.getConnection(url); } public static void close(ResultSet rs, Statement stmt, Connection conn) { try { if (rs ! null) rs.close(); } catch (Exception ignored) {} try { if (stmt ! null) stmt.close(); } catch (Exception ignored) {} try { if (conn ! null) conn.close(); } catch (Exception ignored) {} } }JSP 里就变成先拿路径、再调工具类、最后在finally里统一关闭。这样改的好处是以后要换 UCanAccess 或者换数据库只改DBUtil一个地方页面不用动。参数上唯一需要注意的是dbAbsolutePath必须由调用方通过application.getRealPath算好传进来工具类不依赖 Servlet API方便单独测试。第二个改造点是 SQL 参数化。原代码的select * from username没有用户输入暂时安全但一旦加上按用户名查询拼接字符串就会引入注入风险。Access 的 JDBC-ODBC 桥对PreparedStatement支持有限常见做法是用PreparedStatement加?占位符如果驱动报不支持再退回手工转义单引号。改造时先用一条带参数的查询验证驱动行为别等上线才发现占位符不生效。第三个改造点是配置外置。把 mdb 路径、驱动名写进web.xml的context-param或者一个 properties 文件JSP 启动时读取。这样换机器部署时不用改代码只改配置。验证方法很简单把 mdb 挪到另一个目录只改配置不改 JSP页面还能正常输出说明抽离到位了。最后说一个我踩过的坑改完工具类后忘了把编译好的.class放到WEB-INF/classes对应包路径下Tomcat 启动不报错访问页面才抛ClassNotFoundException。从那以后我每次改完 Java 类都强制走一遍“编译 → 确认 class 文件落在 WEB-INF/classes → 重启 Tomcat → 访问页面看输出”的流程不再靠热部署的玄学。希望帮到你。本文还有配套的精品资源点击获取