带徒弟从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

相关文章

springboot集成easypoi导出word换行处理过程

《springboot集成easypoi导出word换行处理过程》SpringBoot集成Easypoi导出Word时,换行符n失效显示为空格,解决方法包括生成段落或替换模板中n为回车,同时需确... 目录项目场景问题描述解决方案第一种:生成段落的方式第二种:替换模板的情况,换行符替换成回车总结项目场景s

SpringBoot集成redisson实现延时队列教程

《SpringBoot集成redisson实现延时队列教程》文章介绍了使用Redisson实现延迟队列的完整步骤,包括依赖导入、Redis配置、工具类封装、业务枚举定义、执行器实现、Bean创建、消费... 目录1、先给项目导入Redisson依赖2、配置redis3、创建 RedissonConfig 配

SpringBoot中@Value注入静态变量方式

《SpringBoot中@Value注入静态变量方式》SpringBoot中静态变量无法直接用@Value注入,需通过setter方法,@Value(${})从属性文件获取值,@Value(#{})用... 目录项目场景解决方案注解说明1、@Value("${}")使用示例2、@Value("#{}"php

SpringBoot分段处理List集合多线程批量插入数据方式

《SpringBoot分段处理List集合多线程批量插入数据方式》文章介绍如何处理大数据量List批量插入数据库的优化方案:通过拆分List并分配独立线程处理,结合Spring线程池与异步方法提升效率... 目录项目场景解决方案1.实体类2.Mapper3.spring容器注入线程池bejsan对象4.创建

线上Java OOM问题定位与解决方案超详细解析

《线上JavaOOM问题定位与解决方案超详细解析》OOM是JVM抛出的错误,表示内存分配失败,:本文主要介绍线上JavaOOM问题定位与解决方案的相关资料,文中通过代码介绍的非常详细,需要的朋... 目录一、OOM问题核心认知1.1 OOM定义与技术定位1.2 OOM常见类型及技术特征二、OOM问题定位工具

PHP轻松处理千万行数据的方法详解

《PHP轻松处理千万行数据的方法详解》说到处理大数据集,PHP通常不是第一个想到的语言,但如果你曾经需要处理数百万行数据而不让服务器崩溃或内存耗尽,你就会知道PHP用对了工具有多强大,下面小编就... 目录问题的本质php 中的数据流处理:为什么必不可少生成器:内存高效的迭代方式流量控制:避免系统过载一次性

基于 Cursor 开发 Spring Boot 项目详细攻略

《基于Cursor开发SpringBoot项目详细攻略》Cursor是集成GPT4、Claude3.5等LLM的VSCode类AI编程工具,支持SpringBoot项目开发全流程,涵盖环境配... 目录cursor是什么?基于 Cursor 开发 Spring Boot 项目完整指南1. 环境准备2. 创建

C#实现千万数据秒级导入的代码

《C#实现千万数据秒级导入的代码》在实际开发中excel导入很常见,现代社会中很容易遇到大数据处理业务,所以本文我就给大家分享一下千万数据秒级导入怎么实现,文中有详细的代码示例供大家参考,需要的朋友可... 目录前言一、数据存储二、处理逻辑优化前代码处理逻辑优化后的代码总结前言在实际开发中excel导入很

Spring Security简介、使用与最佳实践

《SpringSecurity简介、使用与最佳实践》SpringSecurity是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架,本文给大家介绍SpringSec... 目录一、如何理解 Spring Security?—— 核心思想二、如何在 Java 项目中使用?——

SpringBoot+RustFS 实现文件切片极速上传的实例代码

《SpringBoot+RustFS实现文件切片极速上传的实例代码》本文介绍利用SpringBoot和RustFS构建高性能文件切片上传系统,实现大文件秒传、断点续传和分片上传等功能,具有一定的参考... 目录一、为什么选择 RustFS + SpringBoot?二、环境准备与部署2.1 安装 RustF