带徒弟从java连接access数据过程中发现的疑难问题吸取成长经验

本文主要是介绍带徒弟从java连接access数据过程中发现的疑难问题吸取成长经验,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一.背景

        当然还是公司要求的师带徒任务。公司的化检验室在作业后,数据只能手工填到系统中,打开化检验室电脑发现是用的access数据。我想可以把access数据库的数据自动上传到系统,这样就减少了检化验人员的工作量,也减少了数据因手工录入带来的不必要错误。其实,在很多公司都有类似的边缘系统,用类似这种数据的还很多。只是在过程中出现了意想不到的问题,这个案例值得记录。

二.开发环境

操作系统:win11家庭版

jdk:1.8

开发工具:eclipse(我)、idea(徒弟)

access:2007

三.连接的技术路线选择

       网上资料主要是ODBC-JDBC桥接、JDBC连接两种,参考资料Java 连接Access数据库的两种方式_java_脚本之家。很多年以前,我是用的ODBC-JDBC桥接的方式。我建议徒弟用JDBC的方式。让他先动手,不行我再来。

      JDBC的驱动,网络资料用UCanAccess的居多,如Springboot连接Access2003数据库_springboot连接access数据库-CSDN博客

四.问题的发生

Exception in thread "main" java.io.IOException: Unsupported newer version: 20at com.healthmarketscience.jackcess.JetFormat.getFormat(JetFormat.java:289)at com.healthmarketscience.jackcess.Database.<init>(Database.java:908)at com.healthmarketscience.jackcess.Database.open(Database.java:676)at com.healthmarketscience.jackcess.Database.open(Database.java:615)at com.healthmarketscience.jackcess.Database.open(Database.java:585)at com.healthmarketscience.jackcess.Database.open(Database.java:558)at com.healthmarketscience.jackcess.Database.open(Database.java:534)at com.scantt.sevenstarcolor.AccessJackcessUtils.main(AccessJackcessUtils.java:43)

因为用的数据连接池的方式,启动时报错。一看错误认为是版本的问题,反复换了ucanaccess的多个版本,错误一直都没有变。

五.问题的跟踪解决

1.看源码,加断点,发现惊天密码

在网上收索了一大堆,没有一个资料写到了这个问题。徒弟没有了思路,该我上场了。先看看源代码,发现JetFormat.getFormat主要就是从文件头的字节码来判断access文件的版本,没有找到版本就要报错啊!这个完整的方法如下:

public static JetFormat getFormat(FileChannel channel) throws IOException {ByteBuffer buffer = ByteBuffer.allocate(HEADER_LENGTH);int bytesRead = channel.read(buffer, 0L);if(bytesRead < HEADER_LENGTH) {throw new IOException("Empty database file");}buffer.flip();byte version = buffer.get(OFFSET_VERSION);if (version == CODE_VERSION_3) {return VERSION_3;} else if (version == CODE_VERSION_4) {if(ByteUtil.matchesRange(buffer, OFFSET_ENGINE_NAME, MSISAM_ENGINE)) {return VERSION_MSISAM;}return VERSION_4;} else if (version == CODE_VERSION_12) {return VERSION_12;} else if (version == CODE_VERSION_14) {return VERSION_14;}throw new IOException("Unsupported " +((version < CODE_VERSION_3) ? "older" : "newer") +" version: " + version);}

跟踪代码发现,第21个字节存储的是版本信息。实际buffer的值,第21个字节值为20。

[37, 84, 83, 68, 45, 72, 101, 97, 100, 101, 114, 45, 35, 35, 35, 37, -90, 28, -128, -101, 20]

而代码中各个版本的定义值分别是:

CODE_VERSION_3:0x0

CODE_VERSION_4:0x1

CODE_VERSION_12:0x2

CODE_VERSION_14:0x3

根本没有20这个版本的定义啊!我把access另存为2000、2003、2007等都是报的相同问题,见鬼了,难道字节码还被谁修改了?突然我注意到了文件图标上面的锁,心里一惊,哎呀,可能跟公司安装的监视加密软件有关。。。。

平时,我们的word、ppt等文档都被加密了的,发出去别人看到的是乱码。加密嘛,在存储层面肯定对字节码做了手脚的。为啥我们机器上打开文档不是乱码呢?加密软件应该劫持了微软word、金山wps等软件的进程,在内存中喂数据给软件之前对数据进行了解密,所以在软件中看到的内容就不是乱码了。而我们的开发工具没有被加密软件劫持,我们拿到的就是加密的字节码,当然就是不正确的啦!

2.绕开加密软件,看正常文件的version标识值,验证猜想

既然,我们开发工具eclipse没有被劫持,那么我可以用eclipse创建并写入数据,该文档就应该是未加密状态。代码如下:

String filePath = "D:/test/test1.mdb";
Database db = Database.create(FileFormat.V2000,new File(filePath));Table newTable = new TableBuilder("NewTable").addColumn(new ColumnBuilder("a").setSQLType(Types.INTEGER).toColumn()).addColumn(new ColumnBuilder("b").setSQLType(Types.VARCHAR).toColumn()).toTable(db);newTable.addRow(1,"foo");
db.close();

为了追求快速验证我的想法,代码随便写的,不要吐槽啊!

发现文件图标没有锁的标识了。

再读取一下数据看看呢?代码如下:

String filePath = "D:/test/test1.mdb";
Database db = Database.open(new File(filePath),true);
Table newTable =db.getTable("NewTable");for(Map<String,Object> row: newTable) {System.out.println(row);}
db.close();

读取正常,输出如下:

11:07:41.682 [main] DEBUG com.healthmarketscience.jackcess.PageChannel - Reading in page 1a
{a=1, b=foo}

断点调试,取得文件头21个字节的值为:

[0, 1, 0, 0, 83, 116, 97, 110, 100, 97, 114, 100, 32, 74, 101, 116, 32, 68, 66, 0, 1]

第21个字节的值为1,对应是CODE_VERSION_4,经过查找发现我创建文件的FileFormat.V2000的值相对应。至此,破案了,一切都是加密软件背锅。

六.总结

       徒弟毕业不到一年,能用网上资料搭建环境,连接access已经不错了。出现的搜索不到的问题,我们要大胆的阅读源代码。思维打开,放飞想法,谁能想到开发机器安装了加密监视软件?代码的断点调试,字节码值的妖怪现象还是揭露了加密监视软件的手脚。本来不是问题,有了这样的问题出现,提升了徒弟排查问题的能力,打开了思维。不要怕,再诡异的问题都有原因!

这篇关于带徒弟从java连接access数据过程中发现的疑难问题吸取成长经验的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/900253

相关文章

Java数组初始化的五种方式

《Java数组初始化的五种方式》数组是Java中最基础且常用的数据结构之一,其初始化方式多样且各具特点,本文详细讲解Java数组初始化的五种方式,分析其适用场景、优劣势对比及注意事项,帮助避免常见陷阱... 目录1. 静态初始化:简洁但固定代码示例核心特点适用场景注意事项2. 动态初始化:灵活但需手动管理代

Java使用SLF4J记录不同级别日志的示例详解

《Java使用SLF4J记录不同级别日志的示例详解》SLF4J是一个简单的日志门面,它允许在运行时选择不同的日志实现,这篇文章主要为大家详细介绍了如何使用SLF4J记录不同级别日志,感兴趣的可以了解下... 目录一、SLF4J简介二、添加依赖三、配置Logback四、记录不同级别的日志五、总结一、SLF4J

将Java项目提交到云服务器的流程步骤

《将Java项目提交到云服务器的流程步骤》所谓将项目提交到云服务器即将你的项目打成一个jar包然后提交到云服务器即可,因此我们需要准备服务器环境为:Linux+JDK+MariDB(MySQL)+Gi... 目录1. 安装 jdk1.1 查看 jdk 版本1.2 下载 jdk2. 安装 mariadb(my

SpringBoot中配置Redis连接池的完整指南

《SpringBoot中配置Redis连接池的完整指南》这篇文章主要为大家详细介绍了SpringBoot中配置Redis连接池的完整指南,文中的示例代码讲解详细,具有一定的借鉴价值,感兴趣的小伙伴可以... 目录一、添加依赖二、配置 Redis 连接池三、测试 Redis 操作四、完整示例代码(一)pom.

Java 正则表达式URL 匹配与源码全解析

《Java正则表达式URL匹配与源码全解析》在Web应用开发中,我们经常需要对URL进行格式验证,今天我们结合Java的Pattern和Matcher类,深入理解正则表达式在实际应用中... 目录1.正则表达式分解:2. 添加域名匹配 (2)3. 添加路径和查询参数匹配 (3) 4. 最终优化版本5.设计思

Java使用ANTLR4对Lua脚本语法校验详解

《Java使用ANTLR4对Lua脚本语法校验详解》ANTLR是一个强大的解析器生成器,用于读取、处理、执行或翻译结构化文本或二进制文件,下面就跟随小编一起看看Java如何使用ANTLR4对Lua脚本... 目录什么是ANTLR?第一个例子ANTLR4 的工作流程Lua脚本语法校验准备一个Lua Gramm

Java字符串操作技巧之语法、示例与应用场景分析

《Java字符串操作技巧之语法、示例与应用场景分析》在Java算法题和日常开发中,字符串处理是必备的核心技能,本文全面梳理Java中字符串的常用操作语法,结合代码示例、应用场景和避坑指南,可快速掌握字... 目录引言1. 基础操作1.1 创建字符串1.2 获取长度1.3 访问字符2. 字符串处理2.1 子字

Java Optional的使用技巧与最佳实践

《JavaOptional的使用技巧与最佳实践》在Java中,Optional是用于优雅处理null的容器类,其核心目标是显式提醒开发者处理空值场景,避免NullPointerExce... 目录一、Optional 的核心用途二、使用技巧与最佳实践三、常见误区与反模式四、替代方案与扩展五、总结在 Java

基于Java实现回调监听工具类

《基于Java实现回调监听工具类》这篇文章主要为大家详细介绍了如何基于Java实现一个回调监听工具类,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录监听接口类 Listenable实际用法打印结果首先,会用到 函数式接口 Consumer, 通过这个可以解耦回调方法,下面先写一个

使用Java将DOCX文档解析为Markdown文档的代码实现

《使用Java将DOCX文档解析为Markdown文档的代码实现》在现代文档处理中,Markdown(MD)因其简洁的语法和良好的可读性,逐渐成为开发者、技术写作者和内容创作者的首选格式,然而,许多文... 目录引言1. 工具和库介绍2. 安装依赖库3. 使用Apache POI解析DOCX文档4. 将解析