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

相关文章

解决pandas无法读取csv文件数据的问题

《解决pandas无法读取csv文件数据的问题》本文讲述作者用Pandas读取CSV文件时因参数设置不当导致数据错位,通过调整delimiter和on_bad_lines参数最终解决问题,并强调正确参... 目录一、前言二、问题复现1. 问题2. 通过 on_bad_lines=‘warn’ 跳过异常数据3

Spring WebClient从入门到精通

《SpringWebClient从入门到精通》本文详解SpringWebClient非阻塞响应式特性及优势,涵盖核心API、实战应用与性能优化,对比RestTemplate,为微服务通信提供高效解决... 目录一、WebClient 概述1.1 为什么选择 WebClient?1.2 WebClient 与

Java.lang.InterruptedException被中止异常的原因及解决方案

《Java.lang.InterruptedException被中止异常的原因及解决方案》Java.lang.InterruptedException是线程被中断时抛出的异常,用于协作停止执行,常见于... 目录报错问题报错原因解决方法Java.lang.InterruptedException 是 Jav

深入浅出SpringBoot WebSocket构建实时应用全面指南

《深入浅出SpringBootWebSocket构建实时应用全面指南》WebSocket是一种在单个TCP连接上进行全双工通信的协议,这篇文章主要为大家详细介绍了SpringBoot如何集成WebS... 目录前言为什么需要 WebSocketWebSocket 是什么Spring Boot 如何简化 We

java中pdf模版填充表单踩坑实战记录(itextPdf、openPdf、pdfbox)

《java中pdf模版填充表单踩坑实战记录(itextPdf、openPdf、pdfbox)》:本文主要介绍java中pdf模版填充表单踩坑的相关资料,OpenPDF、iText、PDFBox是三... 目录准备Pdf模版方法1:itextpdf7填充表单(1)加入依赖(2)代码(3)遇到的问题方法2:pd

Java Stream流之GroupBy的用法及应用场景

《JavaStream流之GroupBy的用法及应用场景》本教程将详细介绍如何在Java中使用Stream流的groupby方法,包括基本用法和一些常见的实际应用场景,感兴趣的朋友一起看看吧... 目录Java Stream流之GroupBy的用法1. 前言2. 基础概念什么是 GroupBy?Stream

python设置环境变量路径实现过程

《python设置环境变量路径实现过程》本文介绍设置Python路径的多种方法:临时设置(Windows用`set`,Linux/macOS用`export`)、永久设置(系统属性或shell配置文件... 目录设置python路径的方法临时设置环境变量(适用于当前会话)永久设置环境变量(Windows系统

python运用requests模拟浏览器发送请求过程

《python运用requests模拟浏览器发送请求过程》模拟浏览器请求可选用requests处理静态内容,selenium应对动态页面,playwright支持高级自动化,设置代理和超时参数,根据需... 目录使用requests库模拟浏览器请求使用selenium自动化浏览器操作使用playwright

Mysql中设计数据表的过程解析

《Mysql中设计数据表的过程解析》数据库约束通过NOTNULL、UNIQUE、DEFAULT、主键和外键等规则保障数据完整性,自动校验数据,减少人工错误,提升数据一致性和业务逻辑严谨性,本文介绍My... 目录1.引言2.NOT NULL——制定某列不可以存储NULL值2.UNIQUE——保证某一列的每一

解密SQL查询语句执行的过程

《解密SQL查询语句执行的过程》文章讲解了SQL语句的执行流程,涵盖解析、优化、执行三个核心阶段,并介绍执行计划查看方法EXPLAIN,同时提出性能优化技巧如合理使用索引、避免SELECT*、JOIN... 目录1. SQL语句的基本结构2. SQL语句的执行过程3. SQL语句的执行计划4. 常见的性能优