任务如下:
查找源文件:
然后就是找到这个功能按钮所对应的源文件:
打开源文件:
在eclipse中搜索ImpactAnalysisImportProcessor可找到ImpactAnalysisImportProcessor.java文件,这就是该按钮对应功能的源文件。
开始测试:
首先将pms上面的附件下载下来,然后运行本地测试系统,导入文件测试,并跟着代码debug了一遍,可是没发现任何错误,也能直接生成result文件,并没有存在空指针错误。
于是找到了标哥,标哥说这个问题是发生在生产环境下的,要在生产环境才看的到问题,给了我一个账号,登录生产环境,果然看到了空指针错误:
看到了报错的行数,就开始在java源文件里面看对应的代码。
可是出错的源文件在本地测试环境一直没有任何问题,在生产环境又报错,而且也没有办法对生产环境进行debug,这就是这一次任务最难的点。把整一个源文件看了一遍,也没有发现什么毛病,于是标哥叫我安装一下数据库,在数据库里面看看能不能做文章。于是就开始了安装oracle跟plsql的路。
安装Oracle与plsql
由于我笔记本之前安装过Oracle,就想直接试试能不能用plsql直接连接上,可是试了好多次都不行,只好在标哥那里拿来了相同版本的Oracle以及plsql,把原来的全部卸载掉重新来。
下面是彻底卸载Oracle的方法:https://jingyan.baidu.com/article/922554468d4e6b851648f4e3.html
plsql安装方法很简单,只需要一直点击下一步就可以了。
然后就是安装Oracle:
安装包
在这个安装界面选择第二个,然后一直下一步即可。
环境变量
安装Oracle之后需要配置环境变量ORACLE_HOME和TNS_ADMIN环境变量:(在这里被坑了好久,一点要配置好环境变量,路径要对)
然后打开plsql:
点击取消按钮进入主界面:
点击工具->首选项:
在红框位置配置好两个路径,然后重启plsql。
可是重启之后发现没有读取到远程数据库,一看是因为还没有替换tnsnames.ora文件,于是在标哥那里拿来了这个文件,替换掉D:\app\Simple_Y\product\11.2.0\client_1\network\admin目录下的tnsnames.ora文件,重启plsql,就可以读取到远程数据库了。
数据库连接
这时候在eclipse运行系统,然后导入excel文件解析,查看控制台消息,可以得出是哪一个数据库用户:
然后查看inventory_main项目的pom.xml文件
可以看到对应的数据库用户和密码。然后就用这个账户登录plsql:
连接上数据库之后(记得断vpn),点击下面的小按钮:
选择新建sql窗口,然后就可以对表进行查询了。
查找问题
下面的问题就是找到数据库中对应报错行的那个数据,而Java文件报错的行数为171行,找到对应的代码:
mapmo.put("A_DEVICE", aDevice==null?"":aDevice.getValue("STANDARD_NAME").toString());
意思也不难理解,就是在aDevice对象中获取key值为STANDARD_NAME的value值,放入mapmo对象中,但是debug到这里发现是读取得到对象的,并没有出现空指针错误,所以需要在数据库中找到这个数据,将它置为空,看看能不能出现空指针问题。然后标哥教了我底层元数据api操作数据库的原理:
QueryCriteria qc = new QueryCriteria("SERVICE"); qc.addAssembleFragment("[SERVICE_END_A_DEVICE][SERVICE_END_Z_DEVICE]"); qc.addExpression(QueryExpressionUtil.equals("ID", servId)); List<MetaObject> services = metaService.query(qc).getList(); //上面的就是公司操作数据库的api,一开始一直看不懂 //下面解释具体的意思:
--QueryCriteria qc = new QueryCriteria("SERVICE");
select * from xm_entityspec m where m.code = 'SERVICE';
上面这两句是等同的,在查询结果我们可以看到service对应的表名:
下面的是等同的:
--qc.addAssembleFragment("[SERVICE_END_A_DEVICE][SERVICE_END_Z_DEVICE]");
select * from xm_relationspec m where m.code = 'SERVICE_END_A_DEVICE';
select * from xm_relationspec m where m.code = 'SERVICE_END_Z_DEVICE';
由于我们不知道两个ID分别是对应哪一个,所以需要下面的查询语句来确定:
select * from xm_entityspec m where m.id in (2310000000,1020000000);
在xm_entityspec--实体规格描述表中查询它们分别对应什么:
可以看到两个ID分别对应设备和产品服务以及他们的数据表。
接下来就是:
--根据servid查到service实体的数据
select m.a_device_id,m.* from RM_SERVICE m where m.id = '441020000000001125355060' ;
--根据service实体数据的a_device_id查询device实体的数据
select m.standard_name,m.* from CM_DEVICE m where m.id = 441020000000001083663191;
到这里就可以找到STANDARD_NAME属性的值了,在这里将STANDARD_NAME属性置为空,然后运行系统看看会不会报空指针错误。
--置空
update CM_DEVICE set standard_name = '' where id = 441020000000001163583121;
--查询结果
select m.standard_name,m.* from CM_DEVICE m where m.id = 441020000000001083663191;
ok,成功置空了,然后重新启动系统,清空浏览器缓存,重启数据库,终于如愿以偿地看到了空指针异常。
解决问题
看来已经找到了问题了,让我们再来看看报错的代码行:
mapmo.put("A_DEVICE", aDevice==null?"":aDevice.getValue("STANDARD_NAME").toString());
当aDevice.getValue("STANDARD_NAME")不存在的时候,系统就会发生空指针异常,于是我百度了一下:
map 允许null键null值 你的value引入的是一个变量吧 这个变量获取到的值为空 会报空指针异常,可以对这个变量做一个判断不就行了
是的,问题终于有突破口了,然后就对这一行进行重写:
mapmo.put("A_DEVICE", aDevice==null?"":aDevice.getValue("STANDARD_NAME").toString()); -> if(aDevice!=null && aDevice.getValue("STANDARD_NAME")!=null){ mapmo.put("A_DEVICE", aDevice.getValue("STANDARD_NAME").toString()); }
加入对aDevice.getValue("STANDARD_NAME")的判空处理,重新运行系统,完美通过,终于把这个问题解决了。
然后svn同步本地代码,更新代码,提交自己的修改,在pms网站上提交补丁,问题解决。
这次的问题主要是在数据库方面遇到了不少阻碍,对Oracle数据库无从下手,要看懂元数据操作的api,根据其对应的select语句找到想要的数据,才能开始动手。
下面是标哥说的比较重要的三个表:
xm_entityspec --实体规格描述表xm_relationspec --实体规格关系描述表xm_entitydescriptor --描述实体规格对应哪些数据表,以及数据表有哪些属性底层元数据api的实现,是基于上面3个表的,要了解元数据api,上面3个表是比较重要的