項目是用ssm搭建的。主要是為app數據接口。其中有一個需求就app想要查詢一段時間內某個用戶的測量信息,所以app給我后端傳遞了3個參數,分別是appuserId(String),startDate(Date),endDate(Date)。我在controller中驗證參數沒問題之后我將參數傳遞給service層,然后在service層傳遞給dao層,執行真正的sql語句查詢。
service層:
1 Map<String, Object> conditionMap = new HashMap<>(3); 2 conditionMap.put("appuserId", appuserId); 3 conditionMap.put("startDate", startDate); 4 conditionMap.put("endDate", endDate); 5 6 List<Map<String, Object>> list1 = ecgAutoAnalysisMapper.getHealthDataList(conditionMap);
dao層:
1 List<Map<String, Object>> getHealthDataList(Map<String, Object> conditionMap);
dao層的xml文件:
1 <!--measureStartTime為測量時間,在java中是一個java.util.Date,在mysql中是TIMESTAMP類型--> 2 <resultMap id="healthDataMap" type="map"> 3 <result property="appuserId" column="appuserId" javaType="java.lang.String" jdbcType="CHAR"/> 4 <result property="measureStartTime" column="measureStartTime" javaType="java.util.Date" jdbcType="TIMESTAMP"/> 5 <result property="measureSecondLength" column="measureSecondLength" javaType="java.lang.Integer" jdbcType="INTEGER"/> 6 <result property="hrOverallAvg" column="hrOverallAvg" javaType="java.lang.String" jdbcType="VARCHAR"/> 7 <result property="chiefComplaint_symptom" column="chiefComplaint_symptom" javaType="java.lang.String" jdbcType="VARCHAR"/> 8 <result property="chiefComplaint_diet" column="chiefComplaint_diet" javaType="java.lang.String" jdbcType="VARCHAR"/> 9 <result property="chiefComplaint_activity" column="chiefComplaint_activity" javaType="java.lang.String" jdbcType="VARCHAR"/> 10 </resultMap> 11 12 <select id="getHealthDataList" parameterType="map" resultMap="healthDataMap"> 13 select 14 ea.appuserId,ea.measureStartTime,ed.measureSecondLength,ea.hrOverallAvg,ea.chiefComplaint_symptom, 15 ea.chiefComplaint_diet,ea.chiefComplaint_activity 16 from 17 ecg_auto_analysis ea 18 left join 19 ecg_data ed 20 on 21 ed.autoAnalysisId = ea.id 22 <where> 23 <if test="appuserId != null and appuserId != ''"> 24 and ea.appuserId = #{appuserId,jdbcType=CHAR} 25 </if> 26 <if test="startDate != null and startDate != ''"> 27 and ea.measureStartTime > #{startDate,jdbcType=Date} 28 </if> 29 <if test="endDate != null and endDate != ''"> 30 and #{endDate,jdbcType=Date} > ea.measureStartTime 31 </if> 32 </where> 33 </select>
結果是查詢出來的的結果與實際的不一樣。比如app傳遞參數,appuserId是“393967382ec34f53a53a4d49271a422a”,startDate是“2018-03-09 00:00:00”,endDate是“2018-03-15 23:59:59”,在數據庫中這個用戶在這段時間的測量記錄有7條,而在mybatis中查詢出來的只有4條。很明顯是mybatis的sql語句寫錯了。
數據庫查詢語句及結果(可以看到測量時間集中在兩天,一個是3月9號,一個是3月15號):
1 select 2 ea.appuserId,ea.measureStartTime,ed.measureSecondLength,ea.hrOverallAvg,ea.chiefComplaint_symptom,ea.chiefComplaint_diet,ea.chiefComplaint_activity 3 from 4 ecg_auto_analysis ea 5 left join 6 ecg_data ed 7 on 8 ed.autoAnalysisId = ea.id 9 where 10 ea.appuserId = "393967382ec34f53a53a4d49271a422a" 11 and ea.measureStartTime > "2018-03-09 00:00:00" 12 and "2018-03-15 23:59:59" > ea.measureStartTime
我是這樣解決的,當然故事很曲折,也很折磨人:
1)先注釋掉最后的兩個if語句,mybatis的查詢結果為7,證明問題就出在最后兩個if語句中;
2)通過翻閱以前的代碼,我把這兩個if語句中的 jdbcType=Date 去掉,查詢結果為7,然后加上 jdbcType=Date 查詢結果又為4。沒錯,問題就出在這句話上,為啥不加就好了,加上就不對了;
3)網上查詢資料,對jdbcType這個參數的描述不多,作用不清楚,大概的意思是“jdbcType這個參數的應用場景就是,當執行mapping文件的時候,有個映射的參數為空,那么無法確定他的類型,這個時候就需要jdbcType來確定類型“。可我還是不清除啥原因導致我的查詢結果不正確;
4)最后看了好長時間,我把 jdbcType=Date 改為 jdbcType=TIMESTAMP,查詢結果正確了(這一步花了太多時間)。
我猜想原因是這樣的,當我不設置jdbcType時,最后兩個if語句在執行比較時,會把參數轉換為正確的日期類型,就是 “年月日+時分秒”這種。當我設置 jdbcType=Date 時,就指定日期只用”年月日”,所以傳遞進來的startDate與endDate就變為“2018-03-09 ”,“2018-03-15”,那在此時間段的記錄當然只有4條了。但是設置 jdbcType=TIMESTAMP 時,傳遞進來啥就是啥,所以符合記錄的只有7條了,原因就是這樣。最后看看mysql數據庫中datetime、date、timestamp三種數據類型的區別吧:
1) DATETIME
顯示格式:YYYY-MM-DD HH:MM:SS
時間范圍:[ '1000-01-01 00:00:00'到'9999-12-31 23:59:59']
2) DATE
顯示格式:YYYY-MM-DD
時間范圍:['1000-01-01'到'9999-12-31']
3) TIMESTAMP
顯示格式:YYYY-MM-DD HH:MM:SS
時間范圍:[ '1970-01-01 00:00:00'到'2037-12-31 23:59:59']