Java连接Access数据库实战:UCanAccess 4.0.4环境配置与避坑指南
简介面向需要在 Java 项目中直连 Access 数据库mdb 文件的开发者这份资源提供了一套开源免费的 UCanAccess 4.0.4 解决方案可替代存在查询次数50 次与单次查询条数1000 条限制的 Access_JDBC30.jar通过 JDBC 直连方式访问 mdb无需依赖 JDBC-ODBC 桥接且不受查询次数与条数约束适合中小型系统迁移、报表读取与数据同步等场景。压缩包共 16 个文件约 2.57MB以 6 个 jar 依赖包为核心辅以 8 个 txt 说明与许可文档、1 个 bat 和 1 个 sh 启动脚本覆盖运行所需组件与使用指引jar 之间依赖关系清晰便于按需引入工程。目前已有 741 人学习下载说明其在 Access 直连需求中具备一定参考价值。读者可据此快速搭建无限制的 JDBC 连接环境理解各依赖包的分工与加载方式并参照包内说明完成集成与排错。1. 从一次凌晨两点的连接失败说起Java 读 Access 到底该装什么一个做内部报表的小项目后端要读一个历史遗留的.accdb文件本地用 IDE 跑得好好的打成 jar 丢到测试机上就报No suitable driver found。折腾到凌晨两点才发现问题不在代码而在依赖JDK 里根本没有 Access 的驱动得靠 UCanAccess 这套纯 Java 的 jar 包顶上。Access 数据库在不少老系统里还活着Java 想连它绕不开 UCanAccess而版本选错、jar 少一个报错能让你怀疑人生。这篇记录的就是ucanaccess-4.0.4.jar这套环境怎么装、怎么配、装坏了怎么救适合正在被 Access 连接问题卡住的 Java 开发者新手能照着走通熟手能看清版本和依赖的取舍。2. 装前把环境列清JDK、依赖 jar 与 Access 文件格式2.1 版本对应关系先对齐UCanAccess 不是单个 jar 就能跑起来的它底层依赖 HSQLDB 做内存中转还要 Jackcess 解析 Access 文件另外需要 commons-lang、commons-logging 这类基础库。ucanaccess-4.0.4.jar这个版本对应的是 JDK 7 及以上实际项目里我一般用 JDK 8 跑兼容性最稳。JDK 11 之后模块化带来一些反射限制如果非要用高版本 JDK得留意--add-opens相关的告警这是后话先把 JDK 8 这条最顺的路走通。版本选择上有个反直觉的点不是越新越好。UCanAccess 5.x 之后对 JDK 版本要求提高部分老项目还停留在 JDK 7硬上 5.x 会直接编译不过。4.0.4属于 4.x 系列里比较成熟的一个点配套的 Jackcess 是 2.1.x 系列HSQLDB 是 2.3.x 系列这几个版本号是绑定的不能随手换成最新版否则运行期会出现NoSuchMethodError这种让人抓狂的玄学问题。下面这张表是我整理的环境对照装之前先核一遍组件版本作用是否必需JDK8最低 7运行环境必需ucanaccess4.0.4Access 驱动主包必需jackcess2.1.9 左右解析 accdb/mdb必需hsqldb2.3.4 左右内存数据库中转必需commons-lang2.6基础工具必需commons-logging1.2日志必需commons-lang33.x部分版本需要视情况2.2 确认 Access 文件格式与驱动匹配装之前还有一步容易被跳过确认你手里的 Access 文件是.mdb还是.accdb。UCanAccess 对两者都支持但.accdb是 Access 2007 之后的格式.mdb是老格式底层解析路径不同。如果文件是加密的或者带密码连接串里要额外带密码参数否则会报UCAExc:::4.0.4 given file does not exist或者权限类错误看着像文件不存在其实是认证没过。我一般会先用一个最小 Java 程序验证文件能不能被识别而不是直接塞进业务代码里调试。确认格式这一步花两分钟能省掉后面半小时的瞎猜。3. 下载与依赖装配把 6 个 jar 凑齐的正确姿势3.1 手动下载与目录组织最直接的方式是把ucanaccess-4.0.4.jar和它的依赖 jar 全部下下来放到项目的lib目录。Maven 项目更省事直接在pom.xml里声明让构建工具去拉传递依赖。但要注意UCanAccess 的传递依赖有时候拉不全尤其是老版本仓库里 jackcess 的版本可能被覆盖所以我习惯手动核对一遍依赖树。手动装配的目录结构大概是这样project/ ├── lib/ │ ├── ucanaccess-4.0.4.jar │ ├── jackcess-2.1.9.jar │ ├── hsqldb-2.3.4.jar │ ├── commons-lang-2.6.jar │ ├── commons-logging-1.2.jar │ └── commons-lang3-3.4.jar └── src/把 jar 放进lib之后IDE 里要手动 Add as Library命令行编译运行则要拼 classpath。这一步新手最容易漏漏一个 jar 的报错往往指向别处比如缺 hsqldb 时报的是驱动加载失败看着像主包没装。3.2 Maven 依赖写法与版本锁定Maven 项目里我一般这样写把版本号显式锁死避免传递依赖被别的库顶掉dependency groupIdnet.sf.ucanaccess/groupId artifactIducanaccess/artifactId version4.0.4/version /dependency !-- 显式锁定底层依赖防止版本漂移 -- dependency groupIdcom.healthmarketscience.jackcess/groupId artifactIdjackcess/artifactId version2.1.9/version /dependency dependency groupIdorg.hsqldb/groupId artifactIdhsqldb/artifactId version2.3.4/version /dependency这里的关键是后两个依赖要显式声明。UCanAccess 的 pom 里虽然带了传递依赖但项目里如果同时引了别的库Maven 的最近优先原则可能把 jackcess 换成不兼容的版本运行期就炸。显式写死版本等于给自己留了后悔药。参数上version不要用LATEST或范围写法生产环境锁死具体版本是基本纪律。3.3 纯命令行场景的 classpath 拼法不用 Maven 的老项目编译和运行都要手动拼 classpath。Windows 下分隔符是分号Linux 和 macOS 是冒号这个搞混了会报Could not find or load main class看着像主类写错其实是路径没拼对。# Linux/macOS 下编译注意冒号分隔 javac -cp lib/ucanaccess-4.0.4.jar:lib/jackcess-2.1.9.jar:lib/hsqldb-2.3.4.jar:lib/commons-lang-2.6.jar:lib/commons-logging-1.2.jar -d out src/AccessDemo.java # 运行classpath 要包含 out 目录和所有 jar java -cp out:lib/* AccessDemolib/*这种通配写法在 Java 6 之后支持能省去逐个列 jar 的麻烦但要注意通配符不会递归子目录jar 必须平铺在lib下。Windows 下把冒号换成分号即可。这一步跑通说明依赖装配没问题可以进入连接配置。4. 连接配置与代码落地URL 参数与驱动注册4.1 JDBC URL 的写法与参数含义UCanAccess 的 JDBC URL 格式是固定的写错一个字符就连不上jdbc:ucanaccess://文件路径;memorytrue|false;showSchematrue|falsememorytrue表示把整个 Access 文件加载进内存再操作读多写少的场景下性能更好但文件大了会吃内存memoryfalse是直接操作文件适合大文件或者需要多进程访问的情况。showSchema打开后会在日志里打印表结构调试阶段很有用上线前关掉。我一般读报表类场景用memorytrue因为文件通常不大内存中转快如果是几十万行的历史库就老老实实memoryfalse避免 OOM。这个参数没有绝对对错看文件大小和访问模式。4.2 一段能直接跑的最小连接代码下面这段是我常用的验证模板先跑通再往业务里搬import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.Statement; public class AccessDemo { public static void main(String[] args) throws Exception { // 显式加载驱动老 JDK 或某些容器环境下必需 Class.forName(net.ucanaccess.jdbc.UcanaccessDriver); // memorytrue 表示整库载入内存适合小文件读场景 String url jdbc:ucanaccess:///data/report.accdb;memorytrue; try (Connection conn DriverManager.getConnection(url); Statement stmt conn.createStatement(); ResultSet rs stmt.executeQuery(SELECT COUNT(*) FROM Sheet1)) { if (rs.next()) { System.out.println(行数: rs.getInt(1)); } } } }Class.forName这行在 JDBC 4.0 之后理论上可以省略但 UCanAccess 在部分容器和 OSGi 环境下自动注册会失效显式加载最保险。URL 里的路径用绝对路径相对路径在不同工作目录下解析结果不一样是常见的翻车点。try-with-resources保证连接和语句关闭Access 文件被占用时如果不关连接下次打开会报文件锁错误。4.3 表名与字段名的坑Access 的表名和字段名经常带空格或者中文SQL 里必须用方括号包起来比如SELECT [客户名称] FROM [订单 表]。不加方括号解析器会把空格当成分隔符报语法错误。这一点和 MySQL 的反引号类似但 Access 只认方括号。字段名大小写不敏感但表名在某些版本下敏感统一按文件里的原始写法最稳。5. 避坑与排查五条血泪记录5.1 报 No suitable driver found现象是运行时报java.sql.SQLException: No suitable driver found for jdbc:ucanaccess://...。原因通常是驱动类没加载或者 classpath 里缺 ucanaccess 主包。解决方法是先确认Class.forName(net.ucanaccess.jdbc.UcanaccessDriver)有没有执行再检查 jar 是否真的在 classpath 里。用java -cp跑的时候lib/*通配符在部分 shell 下不展开得手动列全。5.2 报 UCAExc 开头的版本冲突现象是UCAExc:::4.0.4 java.lang.NoSuchMethodError或者NoClassDefFoundError。原因是 jackcess 或 hsqldb 版本被别的依赖顶掉了和 4.0.4 不匹配。解决办法是用mvn dependency:tree看依赖树把 jackcess 和 hsqldb 显式锁到 2.1.9 和 2.3.4排除掉冲突来源。这个坑最隐蔽因为编译期不报错运行到具体方法才炸。5.3 文件被占用或锁死现象是第二次连接时报File is currently locked或者写入失败。原因是上一次的连接没关闭Access 文件被进程持有。解决方法是确保每个Connection都在finally或 try-with-resources 里关闭memorytrue模式下尤其要注意内存库不关会一直占着文件句柄。另外多线程同时写同一个文件也会锁读场景尽量用内存模式规避。5.4 中文乱码现象是查出来的中文字段显示成问号或乱码。原因是 Access 文件的字符集和 JVM 默认编码不一致。解决办法是启动参数加-Dfile.encodingUTF-8同时确认 Access 文件本身存的是 Unicode。老.mdb文件如果是本地编码存的可能需要额外转换这种情况我一般先用 Access 另存为.accdb再读省事。5.5 高版本 JDK 的反射告警现象是 JDK 11 及以上运行时刷一堆WARNING: An illegal reflective access operation has occurred。原因是 UCanAccess 4.0.4 内部用了反射访问 JDK 内部 API模块化之后被限制。解决办法是启动参数加--add-opens java.base/java.langALL-UNNAMED之类的开放指令或者干脆降回 JDK 8。生产环境我倾向后者少一堆告警心里踏实。6. 装完怎么验证三步跑通再进业务装完不能只看编译通过得实际跑一次查询才算数。我的验证习惯分三步每步都有明确的通过标准。第一步驱动加载验证。写一个只有Class.forName的 main 方法跑起来不抛ClassNotFoundException说明主包和依赖都在 classpath 里。这一步排除掉一半的装配问题。第二步连接与元数据验证。连上之后不查业务表先查DatabaseMetaData拿表清单try (Connection conn DriverManager.getConnection(url)) { var meta conn.getMetaData(); try (ResultSet tables meta.getTables(null, null, %, new String[]{TABLE})) { while (tables.next()) { System.out.println(表: tables.getString(TABLE_NAME)); } } }能列出表名说明文件解析、驱动、连接串全链路通了。这一步比直接查业务表更稳因为不依赖具体表结构。第三步真实查询与类型验证。挑一张有中文、有日期、有数字的表各查一个字段确认类型映射正确。Access 的日期字段读出来是Timestamp货币字段是BigDecimal如果映射错位业务层会出静默错误。我一般会打印rs.getObject(i).getClass()核对一遍。三步都过再往业务代码里搬。从那以后我每次接 Access 相关的活都强制先走这三步验证不再直接改业务代码试错省下的调试时间比装环境本身还多。希望帮到你。本文还有配套的精品资源点击获取