问题描述:java
1. 项目分为两个部分,一个后台的数据采集程序,接收来自各个传感器发送过来的数据,解码后以特定的格式存储在数据库对应的表中,另外一个是web部分,用户能够经过web查看这些数据的状况(历史数据和实时数据);web
2. 在存储检测数据的表MonitorData中,以设备ID(区分不一样的物理设备)、类型属性ID(同一个设备能够检测多种数据,好比风速风向传感器就可以检测出风速和风向两种数据,这两种属性就经过类型属性ID来区分)、时间戳(记录后台收到数据的时间)来惟一标识一条数据;sql
3. 在web部分,有一个模块能够经过指定起始时间段来查询历史数据;数据库
4. 好了如今问题来了,如今我经过这个模块查看一段时间内的历史数据,查询结果以下:框架
咦,为何会出现这么多0值呢?来看一下数据库中的数据库(select * from MonitorData):jsp
在数据库中对应的值根本不是0,而web端读到的值竟然是0,这是为何?函数
WHY?sqlserver
带着这个问题,首先找到了web端对应的页面部分:ui
对,就是这个listMonitorData.jsp,让咱们来看一下它的源码,在源码中咱们主要是要找到在后台这个数据请求是交给那一个类的方法处理的,因此咱们找到它的表单提交路径:spa
该web使用了struts2框架,咱们看到,该表单提交给了一个叫listMonitorDataDevice的action来处理,那咱们继续去找该action
直觉告诉我,我要的action颇有可能在struts-device.xml中,由于咱们查询的设备的数据嘛。打开一看,果真有对应的action:
看到class="com.water.struts2.action.device.DeviceAction" method="{1}",咱们知道接下来调用的方法是com.water.struts2.action.device.DeviceAction类中的listMonitorData方法。对method参数不熟的同窗能够参考一下这篇博客。
好了,那让咱们直接找到对应的方法中的抓取数据部分吧
到这里咱们抓取了数据库中这段时间的所有数据的时间戳,下面是读取数据的主要代码,也是出问题的部分:
1 MonitorDataVo mvo = new MonitorDataVo(); 2 if (list != null && list.size() > 0) { 3 for (int m = 0; m < list.size(); m++) { 4 String datatimestamp = StringUtil 5 .getStringValue((list.get(m))); 6 // System.out.println("+++++++datatimestamp: " + datatimestamp); 7 for (int i = 0; i < attributes.size(); i++) { 8 DeviceAttributeVo attriVo = attributes.get(i); 9 // 首先构建ID 10 MonitorDataId id = new MonitorDataId(); 11 id.setDevice(device); 12 DeviceTypeAttribute attr = new DeviceTypeAttribute(); 13 attr.setDeviceTypeAttributeId(attriVo.getDeviceTypeAttributeId()); 14 id.setDeviceTypeAttribute(attr); 15 Date tmpDate = DateTimeUtil.parseDateTimeHMSs(datatimestamp); 16 // System.out.println("=========tmpdate: " + tmpDate.getTime()%1000); 17 id.setDataTimestamp(tmpDate); 18 // 新建 19 MonitorData mdata = new MonitorDataDAO().findById(id); 20 if (mdata == null) { 21 System.out.println("==NULL"); 22 mdata = new MonitorData(); 23 mdata.setDataValue(0.0); 24 } 25 mvo.addMonitorData(mdata); 26 } 27 mvo.setDatatimestamp(DateTimeUtil 28 .parseDateTimeHMSs(datatimestamp)); 29 mvolist.add(mvo); 30 mvo = new MonitorDataVo(); 31 } 32 }
直观上来看,根据上一步获得的时间戳的list,知道了数据的条数,对于每条记录咱们用Hibernate的get方法根据ID查询出对应的value(在findById方法中调用Hibernate的get方法)
MonitorData instance = (MonitorData) getSession().get("com.water.hibernate.MonitorData", id);
这里咱们用了一个MonitorData的类来表示一个数据。attributes属性是一个action类中的全局数据,表示当前设备具备的属性,如风速风向。到这里咱们初步明白了为何有些web页面上有些值是0了,是由于get方法没有根据ID匹配到对象,因此返回了null,继而findById返回了null,继而mdata的value值为0.0,可是为何get方法会查询不到对应的对象呢?咱们所使用的ID是这样的:
private Device device; private DeviceTypeAttribute deviceTypeAttribute; private Date dataTimestamp;
它包含了三个成员变量,device是根据deviceID来查询到的
Device device = new DeviceDAO().findById(deviceId);
而deviceId是这个action类的String型的成员变量,它的赋值是经过反射找到set方法实现的,而deviceTypeAttribute是根据attributes来的,attributes是根据deviceID查询数据库获得的,deviceID一样是这个action类的String型的成员变量,所以这两个变量应该都没有什么问题。datatimestamp是根据前面的时间戳list获得的String,再转化成Date类型而获得的,按理说也没什么问题。
Date tmpDate = DateTimeUtil.parseDateTimeHMSs(datatimestamp);
从现象概括问题的特色,从而发现问题的根源
经过查看源码找问题貌似遇到了点问题,因而咱们回过头来再去看看问题自己。首先咱们观察是具体哪些时间出现了问题,在数据库中对应时间点是否是有什么特征:
咱们发现,在数据库中的时间戳是精确到毫秒的,而出现问题的时间点(也就是经过ByID的get方法找不到的记录)有一个共同的特色,那就是毫秒位为0,或者说按正常书写小数点后末尾的0是能够省去的,也就是达不到三位,好比14.890,能够写成14.89。
根据问题特色猜想引发问题的可能缘由:
出现问题主要是由于findById(id)这个函数不能获得id对应的对象,进一步来讲是Hibernate中的get方法返回了null,而get方法基于的ID中包含了一个datatimestamp的Date对象,凡是datatimestamp中毫秒位为0的ID都没法正常get到数据。而Hibernate的get方法最终会转化成一条SQL语句对数据库进行查询(固然首先是在Session中查找)
Hibernate: select monitordat0_.DeviceID as DeviceID10_0_, monitordat0_.DeviceTypeAttributeID as DeviceTy2_10_0_, monitordat0_.DataTimestamp as DataTime3_10_0_, monitordat0_.DataUpdateTime as DataUpda4_10_0_, monitordat0_.DataValue as DataValue10_0_ from water.dbo.MonitorData monitordat0_ where monitordat0_.DeviceID=? and monitordat0_.DeviceTypeAttributeID=? and monitordat0_.DataTimestamp= ?
因此猜想:
SQL语句在作查询的时候条件对比是如何实现的?尤为是datetime类型的数据对比,最终是转化成格式化的时间字符串进行对比的,仍是转化成毫秒数进行对比,根据问题的特征,有可能最终是转化成格式化时间的字符串进行的对比,也就是说数据库中的时间保留了毫秒位最后的‘0’,而java类中的Date类型最终是简化了写法,把最后的‘0’省去了,所以二者出现了不一致,因此该条记录查询不到。
临时解决方案:
Date dt = new Date(); System.out.println("当前时间" + dt); String tmpDate = String.valueOf(dt.getTime()%10); long tmpDt = dt.getTime(); if(tmpDate.equalsIgnoreCase("0") || tmpDate.equalsIgnoreCase("9") || tmpDate.equalsIgnoreCase("1")) { tmpDt += 3; } Date newDate = new Date(tmpDt); id.setDataTimestamp(newDate);