注册
一个导致应用服务报错转换科学计数法字符串为整型的故障分析
技术分享/ 文章详情 /

一个导致应用服务报错转换科学计数法字符串为整型的故障分析

BruceCD 2025/10/17 200 0 0

问题描述

客户反馈说致远oa协同在执行打开底表查询时报错:
操作异常!for Input string:
“-1.68557142844863E+16”
客户说这种报错有很多记录,报错中的科学计数法的字符串都不太一样。

问题背景信息

数据库为DM8单机环境,应用服务是致远OA协同系统,达梦中的数据是从sqlserver中使用DTS迁移过来的,迁移配置也是使用的最基础的默认配置,没有做特殊配置(比如类型映射、表名映射等)。

问题分析过程

判断客户的推断是否正确

客户第一反应是DTS迁移工具的问题,他觉得是从sqlserver迁移到DM后表中的数据变了,原来应该是整型的数据在迁移过程中被转换为科学计数法的字符串,然后导致应用服务在查询到这条数据时,应用服务的内部逻辑再将此字符串数据转换为整型时报错。
但是这种推断仔细思考一下后不太可能,因为从应用日志中看到的报错信息如下:
image.png
可以看到这个报错最直接的原因是java代码中在将用科学计数法表示的字符串转为Long类型时抛出了格式异常。既然是字符串类型,那DM中对应的表中肯定有字符串类型(VARCHAR、CHAR、TEXT等)字段中存储了科学计数法的字符串数据,然后迁移源端数据库SQLSERVER中源表中对应字段是数字类型(比如INT、NUMERIC、DECIMAL等),但是据我所知DTS在迁移表结构时进行字段类型映射不会做这种映射,但是问题没有排查出来之前先保留这种想法。

在从DM数据库表中查数据的同时客户也申请了致远的支持服务,等他们远程支持。

在等待过程中,我想了一下这个导致报错的科学计数法的字符串数据是哪里来的?

  1. 如果是DM中某张表的字符类型字段中存储的,那就有很大概率和DTS迁移有关,比如表结构迁移时是否有问题、数据迁移时做了什么转换操作等,这个就需要找到源库中这张表的对应字段是什么类型和DTS的一些细节信息。
  2. 如果不是DM中某张表的字符类型字段中存储的,那就可能是表中某个数字类型的值通过驱动被取出来时,被转换为字符串类型;也有可能是其他的什么类型数据经过计算后被转为科学计数法的字符串等。这种情况下和DTS关系就不是很大了,更有可能是应用层的逻辑问题或者数据库驱动问题。

首先按照这个思路,我们需要找到有问题的数据,客户说科学计数法表示的字符串来自是formmain_6664这张表,然后我根据他提供的查询字段信息在DM管理工具中查询出来,并没有发现有这种字符串,接连查了好几条会导致地表查询报错的相关数据记录,也没有发现。既然这张表中没有,会不会其他表的数据?因为考虑到底表查询业务操作可能会对应多条SQL查询,那涉及到表也可能是多个,按照这个想法再去找了几个可能的相关表,但是也都没有发现问题数据,感觉这样找像大海捞针,现在只能等待应用服务的研发人员来分析问题数据的确切来源了。

再看下驱动是否一致,是否使用了和数据库版本一致的DMjdbc驱动,客户将OA应用所在服务器上的驱动文件看了一下文件大小(具体到字节数),对比下来也是一样的。

发现真正问题点

下午致远的支持工程师开始了远程协助,他们对业务系统代码非常熟悉,根据报错的业务层日志,很快定位到数据的来源,是从一个连表查询中查出来的,对应的字段其实是NUMERIC(19,0)类型,其中的数据也是正常的整型数据,并没有科学计数法的字符串存在,这种和我们上面推测的第二种情况吻合了,那就去定位应用逻辑或者驱动问题。

致远的工程师说这个报错的java代码逻辑是将一个object对象通过String.valueOf的方法转为String字符串,然后又用Long.ParseLong的方法转为Long类型时报错的,也就是说String.valueOf(object对象)计算的结果是一个科学计数法的字符串。object对象是通过jdbc的ResultSet.getObject(1)取到的jdbc对象,实际内部存储的是一个NUMERIC(19,0)的数字类型数据,大概的代码逻辑如下:

Object id_object = rs.getObject(1);
Long.parseLong(String.valueOf(id_object));

然后我按照这个逻辑使用最简单的demo进行了模拟,使用的jdbc驱动也是和此环境一样的版本,并没有模拟出来,这个就很奇怪了,难道他们用的驱动不对?但是上午查过OA服务用的驱动是一样的。

实在没有思路后,向公司大佬求助,说明了情况后,他说这种大概率和驱动有关系,可以从v$session系统视图中有个CLNT_VER字段会显示应用所使用的驱动版本。
image.png
经过查询发现OA系统建立的连接用的驱动版本确实是一个老版本驱动,那为什么上午看到的是新驱动呢?

这时候向客户确认了下,他到OA机器上找到了另一个位置的DMJdbcDriver文件,不是上午展示给我看的那个。然后我用java -jar DMJdbcDriver.jar的方式确认这个是老版本的驱动文件,和v$sessions中的CLNT_VER是相同的版本信息。我让他把这个文件传给我,然后用我本地的测试demo跑了一下,复现了此问题,String.valueOf(id_object)得到的结果确实是科学计数法格式的字符串,并且经过多次尝试只有后两位为00的数字才会被转为科学计数法的字符串。

到这儿基本确定问题了,OA用了一个老版本的驱动,从java -jar输出版本信息中看到是2019年的版本,这个版本存在这样的bug:通过String.valueOf(id_object)对末尾两位为00的整数数字会被转为科学计数法的字符串的问题。再和客户继续沟通后,发现上午查过的OA服务用的驱动文件是OA系统本身的,那个确实是新的,但是导致问题DMjdbc驱动文件是后面找到的老版本的驱动文件,它是给东方通中间件用的,所以遗漏了这个地方导致问题定位更曲折了一些,不过对应用的架构也更深入了一些。

驱动问题分析

此版本的驱动是6年多之前的了,从内部的bug列表中查看和这个有关的问题说明,找了一个21年的jdbc驱动测试已经没有这个问题了,说明已经早就被修复了。
这个问题再简化一下相当于是对NUMBERIC类型的后两位为00的数字进行getString方法获取时,获取到的结果是一个科学计数法的字符串。由于没有对这个问题直接的说明,但是看到一个已解决问题提到DECIMAL类型用getString获取时有类似的的问题,个人推断应该是使用了类似于BigDecimal类型的中间类型,在对数据库中查询出的NUMERIC类型数据包装后转换为String时,对某些数据(比如没有小数位的数字的后两位为00)进行判断为需要做科学计数表示。

总结

在这次问题排查中,其实并没有很隐蔽的技术细节问题,反而是沟通和思考方向问题更多一些,总结下来有以下几点体会:

  1. 在问题定位初始阶段要有一个基本的思路,跟着这个思路不断向后拓展,如果没有进展时需要及时停下来反思自己的思路有没有走偏。
  2. 不要完全相信客户的推断,客户有自己的理解是一个有利于问题排查的因素,但是在一个不正确的点上硬找只会离原始问题越来越远。比如这次经历中客户认为是数据转换导致的问题这个推断从根源上就是错误的,但是需要给出让他信服的理由,他才愿意提供更多信息来推进问题定位。
  3. 也不能完全依赖客户提供的辅助信息,必须是有可靠来源的,比如判断文件是否完全相同不能文件大小来确定,最好用md5sum或者sha256sum这样的命令计算校验值更靠谱。
  4. 在对一个不确定的问题点定位时,使用更多的技术手段来给出更多证据也能让客户更相信你的推断,比如jar包版本的查看命令、连接上的jdbc版本查看方式这些都是非常有利的证据。其实对一个java程序是否使用了老版本的jar包驱动,也可以通过lsof的方式查看它打开的是什么路径下jar文件,也可以是辅助证据之一。
  5. 在问题得到解决后要有复盘,总结走的弯路中有哪些是可以避免的,哪些是新的经验,以及后续如何避免此类问题的发生。
评论
后发表回复

作者

文章

阅读量

获赞

扫一扫
联系客服