JSP连接Access数据库实战:UcanAccess驱动选型与避坑指南
发布时间:2026/10/1 4:24:46来源:尧图网络
简介一份面向JSP初学者与Web开发入门者的数据库连接教程文档系统讲解通过JDBC-ODBC桥驱动连接Access数据库并读取数据的方法。文档以test.mdb数据库为例演示创建username表及uid、pwd字段并说明将数据库文件部署至Tomcat指定目录的注意事项随后给出完整JDBCAccess.jsp示例代码覆盖加载驱动、建立连接、执行查询、遍历结果集及资源关闭等关键步骤。同时点明Access在小型项目中的适用场景以及迁移至MySQL等企业级数据库时的思路差异有助于夯实JDBC基础。资源为单个doc文档共1个文件压缩包大小31KB正文包含代码注释与关键点说明便于随时查阅。目前已有238人学习下载适合正在学习JSP数据库编程或准备课程设计的读者参考。1. JSP连接ACCESS数据库这个老组合为什么还没退休JSP连接ACCESS数据库放在今天的视角看远不如“Spring Boot MySQL”那么主流但在一个特定人群里它仍然是最直接的答案课程设计、Java Web入门、局域网内的小型管理系统。Access数据库不需要安装独立服务拷一个.mdb或.accdb文件就能当数据库用JSP的优势是部署轻页面写好丢进Tomcat的webapps就能跑。两个都不重的方案凑在一起能让一个增删改查系统在半天内出原型这也是“数据库课程设计”和“基于jsp的毕设选题”里反复出现这套组合的原因。本文按实际开发顺序讲先选对驱动再做环境匹配然后给可运行代码最后把文件锁、注入和64位驱动这些坑挨个拆开。2. 驱动怎么选JDBC-ODBC桥、UcanAccess与其他方案的取舍2.1 JDBC-ODBC桥能连但JDK 8之后越走越窄Java 8及更早版本内置了一个叫sun.jdbc.odbc.JdbcOdbcDriver的桥接驱动它把JDBC调用翻译成ODBC调用再由Windows的ODBC去访问Access。使用它的前提是先在控制面板的“ODBC数据源管理器”里建一个DSN然后连接串写成下面这样Connection conn DriverManager.getConnection(jdbc:odbc:student_dsn);这段代码在JDK 8里能跑通但问题也埋在JDK版本上JDK 8以后官方移除了这个桥接驱动JDK 11、17的项目里再用Class.forName(sun.jdbc.odbc.JdbcOdbcDriver)会直接报ClassNotFoundException。更隐蔽的是32位与64位系统环境不互通64位JVM里的桥接驱动找不到32位ODBC数据源管理器里建好的DSN反之亦然这种错位造成的“找不到数据源”会让新手反复重装驱动玄学味道很重。桥接方案还有个老大难中文乱码。ODBC桥在读取Access的中文文本字段时经常出现乱码需要额外调整连接串参数而这个参数在不同Windows版本上的表现并不一致。所以我的结论很简单如果JDK恰好是8临时演示一下连接可以一试如果JDK是9以上或者项目里要写中文数据就别在ODBC桥上浪费时间。2.2 UcanAccess纯Java驱动绕开系统环境UcanAccess是一个第三方JDBC驱动核心原理是用Jackcess库直接读写Access文件格式中间不经过ODBC因此不存在32位、64位系统环境错位的问题也没有DSN的概念。连接串格式固定为Connection conn DriverManager.getConnection( jdbc:ucanaccess://C:/data/student.accdb, , );它同时支持.accdb和.mdb两种文件分别对应Access 2007之后的版本和2003及之前的版本。除了最基本的增删改查它还支持多表JOIN、Access的IIF函数等查询语法对JSP页面来说已经够用。使用UcanAccess需要往项目里引入一组依赖核心驱动包ucanaccess底层文件读取库jackcess以及它运行时依赖的commons-lang3、commons-logging和hsqldb。为什么不只引入一个jar因为UcanAccess底层会把“.accdb”中的数据映射到HSQLDB内存表上做SQL解析这几个依赖是它运行的一部分缺一个就报NoClassDefFoundError。如果不用Maven就把这些jar全部放进WEB-INF/lib如果用Maven让Maven自己解析传递依赖。2.3 版本兼容边界ACCDB、MDB与驱动选型结论决定用哪个驱动之前先看Access文件的版本。Access 2003及更早生成的是.mdb文件Access 2007以后默认是.accdb也保留了另存为.mdb的能力。两个格式有区别.mdb兼容性好但附件类型、多值字段等新特性不可用.accdb功能全但旧版Access打不开。从驱动适配角度看UcanAccess对两类文件都支持并且在只读场景下不要求本机安装Access软件。也就是说你在开发机上用Access 2016建好的表拿到只有Tomcat没有Office的服务器上只要路径指向数据库文件照样能查询。这里有个容易被忽略的点Access文件所在目录需要对Tomcat进程放开写权限因为UcanAccess默认以读写模式打开文件并可能产生临时文件如果目录只读连接能建起来但执行INSERT时会报文件只读。驱动选型结论用两句话概括新项目一律用UcanAccess不要再碰ODBC桥数据库文件优先用.accdb字段类型更丰富也避免在老Office上被误改结构。3. 环境准备与路径规划JDK、Tomcat与数据库文件的摆放3.1 JDK版本与Tomcat版本怎么匹配连接Access不挑Tomcat版本但JSP项目本身挑。我见过最多的组合是JDK 8 Tomcat 8.5以及JDK 11 Tomcat 9。JDK 8搭配Tomcat 8.5最容易因为Java Web的Servlet版本要求比较宽松JDK 11搭配Tomcat 9也没问题多数JSP项目跑在上面正常。安装完Tomcat后先确认JAVA_HOME环境变量指向了正确的JDK否则启动startup.bat会闪退日志里只有一句“找不到Java环境”。环境验证用两条命令java -version catalina.bat version如果catalina.bat version能打印Tomcat版本号说明JAVA_HOME配好了。接下来把Tomcat解压到一个不带空格的路径比如D:\dev\tomcat避免路径空格在后续部署war包时产生奇怪问题。3.2 数据库文件放在哪里绝对路径、相对路径与外部路径这是很多人翻车的地方。Access数据库文件的摆放位置直接决定后来部署war包时会不会把数据弄丢。常见有三种摆法第一放在项目资源目录里比如src/main/resources/db/student.accdb。开发时没问题但打成war包后文件会被打包进war内部每次重新部署war都会覆盖掉旧文件数据库里积累的数据跟着丢属于危险做法。第二放在Tomcat安装目录下自定义一个data目录比如D:\dev\tomcat\data\student.accdb。部署简单但Tomcat整目录重装时数据同样会没。第三放在服务器固定数据目录比如Windows上的D:\data\或Linux上的/var/data/。这是我在实际项目里最常用的方式war更新不影响数据备份也方便。代码里建议先用配置文件把路径管理起来而不是把绝对路径硬编码进Java类。# db.properties db.file.pathD:/data/student.accdb db.user db.password提示UcanAccess的连接串接收的是文件路径不接相对路径。有同学试图写jdbc:ucanaccess://student.accdb让它在当前目录找文件这是不行的。实在需要相对定位时可以使用ServletContext.getRealPath(/)拿到部署目录再拼文件相对位置但war解压场景下getRealPath指向Tomcat临时目录有被清理的风险生产环境慎用。3.3 IDEA新建JSP项目从零搭一个web工程并引入UcanAccess在IntelliJ IDEA里新建传统Java Web项目不依赖Maven骨架的做法是File-New Project选Java建好普通Java工程后通过Project Structure添加Web模块勾选创建web.xml。完成后的目录结构是这样java-web-demo/ src/ main/ java/ com/example/db/ConnectionUtil.java webapp/ WEB-INF/ web.xml student.jsp add_action.jsp如果不想手动管理jar我建议直接建Maven工程在pom.xml里引入UcanAccess全家桶代码里干干净净不塞jardependency groupIdnet.sf.ucanaccess/groupId artifactIducanaccess/artifactId version5.0.1/version /dependency这个坐标会传递引入jackcess、commons-lang3、commons-logging、hsqldbMaven中央仓库里检索对应版本即可。如果你对Maven不熟也可以走传统路线从文件管理器里下载需要的jar放进WEB-INF/libIDEA会自动把这个目录识别为依赖库。两种方式都能跑区别只在于后续打包war时要不要处理传递依赖。4. 最小代码跑通UcanAccess下的查询与增删改4.1 连接工具类与单表查询先写一个连接工具类。Access数据库在文件锁上有特殊性连接不能长期持有所以这里不搞全局单例连接而是每次请求开连接、用完后立即关闭。package com.example.db; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.util.Properties; public class ConnectionUtil { private static final String DB_PATH D:/data/student.accdb; static { try { Class.forName(net.ucanaccess.jdbc.UcanaccessDriver); } catch (ClassNotFoundException e) { throw new ExceptionInInitializerError(e); } } public static Connection getConnection() throws SQLException { String url jdbc:ucanaccess:// DB_PATH; Properties props new Properties(); // Access默认无账号密码也可以指定user/password参数 props.put(user, ); props.put(password, ); // 允许读取列名和表注释便于ResultSet按列名取值 props.put(showSchema, true); Connection conn DriverManager.getConnection(url, props); return conn; } }这段代码里最值得注意的机制是UcanAccess每次创建连接时都会去读Access文件结构并建立内存映射这就决定了它不适合长连接复用。把连接放在请求内创建、请求结束关闭是贴合它运作方式的写法而不是像MySQL那样用连接池压满连接数。接下来在JSP页面上做查询展示。这里我用单表student举例字段包括id、name、age、class_name% page importjava.sql.*, com.example.db.ConnectionUtil % % Connection conn null; Statement stmt null; ResultSet rs null; try { conn ConnectionUtil.getConnection(); stmt conn.createStatement(); rs stmt.executeQuery( SELECT id, name, age, class_name FROM student ORDER BY id ); % table border1 tr thID/thth姓名/thth年龄/thth班级/th /tr % while (rs.next()) { % tr td% rs.getInt(id) %/td td% rs.getString(name) %/td td% rs.getInt(age) %/td td% rs.getString(class_name) %/td /tr % } % /table % } finally { // 由内向外依次关闭避免连接没释放造成文件锁定 if (rs ! null) rs.close(); if (stmt ! null) stmt.close(); if (conn ! null) conn.close(); } %这段JSP的要点在finally块里关闭顺序必须由内向外先ResultSet再Statement最后Connection。如果只关连接不关ResultSet连接被占用的时间会拉长在并发访问时极容易造成Access文件锁冲突。这也是连接池等技巧在Access场景下失效的根源后面第6章会展开。4.2 新增、修改、删除PreparedStatement的写法新增操作不能再用字符串拼接SQL了这里用PreparedStatement同时解决SQL注入和Access对单引号的转义问题。以下是一个新增学生记录的add_action.jsp页面处理逻辑% page importjava.sql.*, com.example.db.ConnectionUtil % % request.setCharacterEncoding(UTF-8); String name request.getParameter(name); int age Integer.parseInt(request.getParameter(age)); String className request.getParameter(class); Connection conn null; PreparedStatement ps null; try { conn ConnectionUtil.getConnection(); String sql INSERT INTO student(name, age, class_name) VALUES(?, ?, ?); ps conn.prepareStatement(sql); ps.setString(1, name); ps.setInt(2, age); ps.setString(3, className); ps.executeUpdate(); response.sendRedirect(student.jsp); } finally { if (ps ! null) ps.close(); if (conn ! null) conn.close(); } %通过setString和setInt传参的写法能避免两个问题一是name里带单引号直接拼接SQL会破坏语法二是用户输入的恶意内容被当成SQL执行。这条细节对Access场景尤其重要因为不少人在课程设计里图省事用字符串拼接结果碰上了access注入的经典翻车。修改和删除的写法同理只是SQL语句不同// 修改时按id定位所有参数字段都用set方法 String updateSql UPDATE student SET name?, age?, class_name? WHERE id?; ps conn.prepareStatement(updateSql); ps.setString(1, name); ps.setInt(2, age); ps.setString(3, className); ps.setInt(4, id); ps.executeUpdate(); // 删除时也要用参数化id从request里取值 String deleteSql DELETE FROM student WHERE id?; ps conn.prepareStatement(deleteSql); ps.setInt(1, id); ps.executeUpdate();到这一步一个能用的JSPAccess增删改查架子已经出来了。接下来看看实际部署时最容易踩的坑每个都是我在项目里血泪换来的。5. 连接避坑手册文件锁、注入与64位驱动的排查记录5.1 系统找不到指定的文件路径分隔符与中文路径现象项目在本机能用换到服务器上连接Access时抛java.io.FileNotFoundException提示D:\data\student.accdb (系统找不到指定的文件)但开发机路径并没有问题。原因UcanAccess连接串要求绝对路径而Windows下的反斜杠\在Java字符串里会被当成转义符。很多人写jdbc:ucanaccess://D:\data\student.accdbJVM解析后路径已经变了。另外服务器上数据库目录可能没建或者中文路径造成编码不一致。解决统一用正斜杠D:/data/student.accdb或者写双反斜杠D:\\data\\student.accdb。数据库文件先手动放到目标目录再启动Tomcat避免路径存在但文件缺失的情况。中文路径能避就避Access对非ASCII路径的处理在不同Windows版本上表现不一不值得去赌。5.2 数据库被锁定并发访问与ACCDB文件锁现象页面第一次打开正常刷新一次或同时打开两个标签页时某一个页面长时间转圈日志里出现Could not open database in exclusive mode或者The database is currently opened by another user。原因Access本身是文件型数据库读写时会对整个文件加锁。UcanAccess默认以读写模式打开文件多个并发连接同时写时第二个连接需要等锁释放。JSP代码里如果连接没及时关闭锁释放不了后续请求全部排队表现就是页面卡死。解决第一确保每个请求的Connection都放在finally块里关闭不遗漏任何异常分支第二把并发控制放在业务层写操作尽量串行化比如用队列或定时任务集中写第三确认没有外部程序也在打开同一个Access文件开发机上常见的现象是Access程序自己开着这个文件导致Tomcat读不了。5.3 access注入字符串拼接SQL的后果与参数化修复现象登录页面输入用户名 or 11密码任意就能绕过验证进入系统或者搜索框输入直接触发SQL语法错误。原因页面代码里用字符串拼接把用户输入直接组装进SQL比如SELECT * FROM user WHERE name name 。Access本身不提供存储过程的预编译保护如果应用层不做参数化注入风险跟其他数据库一样存在。这不是Access的问题是写法的失误。解决用PreparedStatement参数化杜绝拼接。同时给JSP页面的输入做最基本的类型校验比如年龄字段解析为int失败时跳转到错误提示页而不是继续执行SQL。补一句排查技巧看到某个输入框加了引号就报500几乎可以断定SQL拼接成功了先改代码再谈其他加固。5.4 64位驱动不匹配ODBC方案的位数问题现象用JDBC-ODBC桥连接时开发机上明明建好了DSN运行却报[Microsoft][ODBC 驱动程序管理器] 未发现数据源名称并且未指定默认驱动程序或者ClassNotFoundException: sun.jdbc.odbc.JdbcOdbcDriver。原因第一个错误是数据源位数与JVM位数不匹配。Windows自带32位和64位两套ODBC数据源管理器64位Tomcat连接不到32位DSN。第二个错误是JDK版本太新桥接驱动已经被移除官方不再提供。解决放弃ODBC桥改用UcanAccess这能同时绕开位数和JDK版本两个问题。如果一定只能用桥接方案那就固定JDK 8并且确保Tomcat的位数和ODBC数据源管理器位数一致这个排查过程相当折腾踩过一次就没第二次了。这条经验对“64位驱动”的质疑很有针对性纯Java驱动才是这套组合最可靠的常态解。6. 进阶与边界连接池的使用上限、war包部署与数据同步判断6.1 连接稳定性验证技巧与连接池的真实边界交给你一个我常用来验证连接是否稳定的小方法写一个循环脚本连续连开Access连接200次每次执行一个简单查询后立即关闭统计失败次数。public class StressCheck { public static void main(String[] args) throws Exception { int fail 0; for (int i 0; i 200; i) { try (Connection conn ConnectionUtil.getConnection(); Statement stmt conn.createStatement(); ResultSet rs stmt.executeQuery(SELECT COUNT(*) FROM student)) { rs.next(); } catch (Exception e) { fail; } } System.out.println(fail fail); } }如果fail大于0优先检查上一章说的文件锁和路径问题而不是怀疑驱动本身。这个脚本比手动刷新页面靠谱得多能快速把“环境问题”和“代码问题”区分开。至于连接池UcanAccess的底子决定它不能像MySQL那样放开用连接池。Access文件锁在并发写时是硬瓶颈连接池开到10个反而会让文件锁冲突概率变大。我的经验是只读查询场景可以用连接池减少重复打开文件的开销但池子最大连接数控制在2到3个写操作场景不要池化保持“请求内开、请求结束关”。6.2 传统JSP项目打包war与部署要点传统JSP项目打包war用IDEA的Build Artifacts就能生成。打包前确认一件事Access数据库文件不要放在war包里。做法是war包里只放Java类和JSP页面数据库放在服务器固定目录路径通过配置文件注入。部署时把war丢进Tomcat的webapps目录Tomcat自动解压访问如http://localhost:8080/java-web-demo/student.jsp。这里经常有人问“nginx支持jsp吗”。nginx自己不认识JSP它只能静态转发或反向代理实际部署是nginx做前端入口动态请求转发到后端的Tomcat。如果你只在内网使用完全可以直接用Tomcat访问不必引入nginx这层。6.3 数据同步边界与退出判断Access做Web后端数据量超过几十万行或者并发写超过个位数性能下降会很明显。如果业务提出要接入数据库同步工具做增量同步或者要求多台应用服务器同时读一个Access文件这说明方案已经到边界了别硬撑。我现在的处理习惯是新项目第一选择仍然是MySQL只有数据量小、单机使用、要快速交付课程设计或内部小工具时才用JSPAccess。把风险点和边界提前说清楚对方不误会自己后续也不背锅。每一步踩坑走过最后沉淀下来的是连接串用绝对路径、驱动用UcanAccess、SQL全部参数化、连接用完立关。这套组合到现在依然能应付那些小项目的需求希望帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网