为什么用Apache DBUtils来处理结果集得到数组时,会抛出LocalDateTime can’t convert to java.util.Date异常
首先说结论,带着结论看会不那么懵逼:Apache DBUtils处理结果集用的是getObject()方法,我们自己处理结果集用的是getDate()方法,前者返回的是LocalDateTime类型,后者返回的是Date类型。
在使用Apache DBUtils之前,我模拟了一下它的底层实现,是用一个能够映射表信息的类的集合ArrayList来存储结果集ResultSet,代码如下
这是我的Actor映射类:
public class Actor{
private int id;
private String name;
private String sex;
private Date borndate;
private String phone;
/*get,set,构造器省略,构造器必须含有无参构造器,因为Apache dbutils底层利用反射通过Actor的无参构造器创建了对象*/
}
这是模拟APache dbutils底层的代码:
public static void main(String[] args) {
//传统的解决结果集不能高效利用的方法(创建一个对象集合保存返回的查询结果)
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
ArrayList<Actor> actors = new ArrayList<>();
try {
connection = JDBCUtilsByDruid.getConnection();
String sql = "select *from actor where id >= ?";
preparedStatement = connection.prepareStatement(sql);
preparedStatement.setInt(1,3);
resultSet = preparedStatement.executeQuery();
while (resultSet.next()){
int id = resultSet.getInt(1);
String name = resultSet.getString(2);
String sex = resultSet.getString(3);
Date borndate = resultSet.getDate(4);
String phone = resultSet.getString(5);
actors.add(new Actor(id,name,sex, borndate,phone));
System.out.println(id + "\t" + name + "\t" + sex + "\t" + borndate + "\t" + phone);
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
try {
System.out.println(resultSet.getClass());
JDBCUtilsByDruid.closeConnection(resultSet,preparedStatement,connection);
} catch (SQLException e) {
e.printStackTrace();
}
}
for (int i = 0; i < actors.size(); i++) {
System.out.println(actors.get(i));
}
这是我数据库中的actor表的列信息:
注意 : 代码中映射类的borndate属性是Date类型的,我用getDate()方法从结果集中获取,虽然我结果集中的borndate在数据库是DateTime类型的,对应到java需要用LocalDateTime类型属性来获取,但是可以通过getDate()方法只获取到Date,相当于获取数据库中的Date类型,所以不会抛出异常。
但是在用Apache DBUtils工具类时,却抛出了如下异常:
Exception in thread "main" java.sql.SQLException: Cannot set borndate: incompatible types, cannot convert java.time.LocalDateTime to java.util.Date Query: select borndate from actor where id > ? Parameters: [1]
这是类转换异常:LocalDateTime类型无法转换成Date类型。显然,我在结果集中获取的对象是LocalDateTime型的,所以在赋值时才会报错。在Debug时,我发现确实是在将数据库中的borndate赋值给java的borndate时抛出了异常:
if (this.isCompatibleType(value, firstParam)) {
setter.invoke(target, value);
} else {
throw new SQLException("Cannot set " + prop.getName() + ": incompatible types, cannot convert " + value.getClass().getName() + " to " + firstParam.getName());
}
上面的target便是映射类对象,value便是从结果集获取的borndate,但是此时的borndate已经是LocalDateTime类型了,所以程序进入else代码块报错。那么我么就要看这个value为什么是LocalDateTime类型。或者说看究竟是不是用getObject()方法获取的。
private <T> T populateBean(ResultSet rs, T bean, PropertyDescriptor[] props, int[] columnToProperty) throws SQLException {
for(int i = 1; i < columnToProperty.length; ++i) {
if (columnToProperty[i] != -1) {
PropertyDescriptor prop = props[columnToProperty[i]];
Class<?> propType = prop.getPropertyType();
Object value = null;
if (propType != null) {
value = this.processColumn(rs, i, propType);
if (value == null && propType.isPrimitive()) {
value = primitiveDefaults.get(propType);
}
}
this.callSetter(bean, prop, value);
}
}
return bean;
}
可以看到value是通过processColumn()方法赋值的,那么就进入这个方法:
protected Object processColumn(ResultSet rs, int index, Class<?> propType) throws SQLException {
//这里用的是getObject(),返回的是LocalDateTime类型对象
Object retval = rs.getObject(index);
if (!propType.isPrimitive() && retval == null) {
return null;
} else {
Iterator var5 = columnHandlers.iterator();
while(var5.hasNext()) {
ColumnHandler handler = (ColumnHandler)var5.next();
if (handler.match(propType)) {
retval = handler.apply(rs, index);
break;
}
}
return retval;
}
}
这时就可以看到,value是用的getObject()方法从数据库中获得borndate对象,而数据库中的borndate是DateTime类型,通过getObject()方法得到的是LocalDateTime类型,所以在将value赋值给target时,出现了类型转换异常。