错误现象
今天调试一段代码,里面有个逻辑,要批量取数据;当运行取到第二批数据的时候,程序报了如下错误:
org.springframework.jdbc.UncategorizedSQLException: StatementCallback; uncategorized SQLException for SQL [select * from (select t.*,rownum rnm from (select distinct vc.id, vc.member_id, vc.order_id, vc.attribute, vc.un_service_day, vc.benefit_customer_id, vc.gmt_target_end, vc.biz_status, vc.ultimate_price, vc.payment_amount, vc.num from ord_order_item vc where biz_status in ('service', 'issue_ready') and gmt_target_end < trunc(sysdate, 'dd') and is_deleted = 'n' and vc.parent_id + 0 = 0 ) t where rownum<=18000 ) where rnm>9000]; SQL state [null]; error code [17026]; 数字溢出; nested exception is java.sql.SQLException: 数字溢出
第一批是rownum<=9000,rnm>0是能正常运行 的。
解决方案
找到出错的代码进行debug,一层层跟进去后,发现在上面的sql执行后,会把数据做一个如下的操作:
List results = (this.rowsExpected > 0 ? new ArrayList(this.rowsExpected) : new ArrayList()); int rowNum = 0; while (rs.next()) { results.add(this.rowMapper.mapRow(rs, rowNum++)); }这个rowMapper.mapRow的操作是:
Map<String,Object> resultMap = new HashMap<String, Object>(); resultMap.put("id", rs.getInt("ID")); resultMap.put("orderId", rs.getInt("ORDER_ID")); resultMap.put("attribute", rs.getString("ATTRIBUTE")); resultMap.put("memberId", rs.getString("MEMBER_ID")); resultMap.put("unServiceDay", rs.getBigDecimal("UN_SERVICE_DAY")); resultMap.put("benefitCustomerId", rs.getInt("BENEFIT_CUSTOMER_ID")); resultMap.put("gmtTargetEnd", rs.getDate("GMT_TARGET_END")!=null?DateUtils.truncate(rs.getDate("GMT_TARGET_END"),Calendar.DAY_OF_MONTH):null); resultMap.put("bizStatus", rs.getString("BIZ_STATUS")); resultMap.put("ultimatePrice", rs.getBigDecimal("ULTIMATE_PRICE"));这个映射如果不成功,就会抛java.sql.SQLException异常。仔细想了想数字溢出,会不会是上面的类有数据转成对应数据类型的时候报出来的?
int类型的数据范围是:-2147483648...2147483647。数据库里面再查了下转化类里面的int的字段最大值,果然发现有条记录的ID为31342135233,大大超过了整型的数据范围,所以程序执行到rs.getInt("ID")时就出错了。
把出错ID修改后,程序执行就正常了!
问题反思
1,.看来数字溢出这种出错,有数字对应的类型溢出的意思,不过出错信息还是少了点,而且抛的异常也没有达成对应的出错类,对应调用方查找出错原因花了一些时间。虽然是javaSql自带的出错信息,但如果能稍微封装下也明确不少阿。
2.数据库使用的是oracle,id定义的类型为number,没有定义精度,位数。一般定义Number的方法:Number(p,s),其中p,s都是可选的:
a、p代表精度,默认为38
b、s代表小数位数,取值范围-84~127,默认取值要看是否指定了p,如果制定了p,默认s为0,如果没有指定p,默认取最大值。
3.再复习下数据库字段定义的相关知识:
1> NUMBER(p,s):固定精度数字类型
2> NUMBER:不固定精度数字类型,当不确定数字的精度时使用,PK通常使用此类型
3> DATE:当仅需要精确到秒时,选择DATE而不是TIMESTAMP类型
VARCHAR2:变长字符串,最长4000个字节
4> CLOB:当超过4000字节时使用,但是要求这个字段必须单独创建到一张表中,然后有PK与主表关联。此类型应该尽量控制使用