Oracle 1.8 CachedRowSet.populate执行systimestamp查询报错咨询
我之前也碰到过几乎一模一样的问题,咱们来好好捋捋原因和解决办法:
你遇到的这个java.sql.SQLException: Invalid SQL type for column异常,核心原因是Oracle的systimestamp返回的是TIMESTAMP WITH TIME ZONE类型,而你用的ojdbc6.jar在配合JDK 1.8的RowSet实现时,对这种Oracle特定的时间类型的映射存在兼容性bug。
RowSet.populate()方法在处理ResultSet时,会依赖JDBC驱动返回的SQL类型码来做类型适配,但ojdbc6并没有正确注册TIMESTAMP WITH TIME ZONE对应的JDBC类型,导致RowSet无法识别这个列的类型,直接抛出异常。而其他查询语句(比如查普通DATE类型的sysdate)没问题,就是因为那些类型的映射是完善的。
这里有几个经过验证的方案,你可以根据自己的场景选择:
修改查询语句,转换数据类型
最简单的办法就是把systimestamp转成普通的TIMESTAMP类型,让驱动能正常处理:select cast(systimestamp as timestamp) from dual这样返回的结果就是标准的TIMESTAMP类型,
RowSet.populate()就能顺利识别并处理了。升级JDBC驱动版本
ojdbc6是针对JDK 6/7设计的,在JDK 8下对一些Oracle特定的类型支持不够完善。你可以换成ojdbc7.jar或者ojdbc8.jar,高版本的驱动已经修复了这类类型映射的问题,能正确识别TIMESTAMP WITH TIME ZONE类型,直接用原来的查询语句也不会报错。手动遍历ResultSet填充RowSet
如果暂时没法改查询语句或者升级驱动,那就绕过populate()方法,自己手动处理ResultSet的数据:// 假设你用的是CachedRowSet CachedRowSet rowset = new CachedRowSetImpl(); rowset.setType(ResultSet.TYPE_SCROLL_INSENSITIVE); rowset.setConcurrency(ResultSet.CONCUR_READ_ONLY); rowset.setTableName("dual"); while (rs.next()) { rowset.moveToInsertRow(); // 这里可以根据需求选择getTimestamp或者getString来获取值 rowset.setTimestamp(1, rs.getTimestamp(1)); rowset.insertRow(); rowset.moveToCurrentRow(); }这种方式完全由你控制类型转换,不会触发驱动和RowSet之间的类型识别问题。
内容的提问来源于stack exchange,提问作者Jefferson

