小酒一杯品源码-DbUtils代码解读

来源:互联网 发布:js输入框输入数据事件 编辑:程序博客网 时间:2024/04/28 02:04

ORM一直是Web开发一个热点话题,DbUtils则是给出了一个相当简洁的答案。DbUtils的嵌套也不深,而且主动的API调用也非常符合程序员的思维(Hibernate和iBatis这种隐藏了大多数细节的框架,连找到个入口都要费半天劲)。

话说最常用的CRUD,使用JDBC最痛的无非是将ResultSet转换为JavaBean。DbUtils则是正好命中这个要害,使用ResultSetHandler机制来解决这个问题。

之前用过Spring JDBC Template,差不多也是这个机制。DbUtils的亮点则是BeanHandler,可以无需手写转换函数,自动根据class生成一个handler。

BeanProcessor的核心代码做了几件事:

  • 提取Bean的字段信息,结果集的字段信息,并作映射;

  • 对Bean的字段做类型转换

字段映射的代码:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
protectedint[] mapColumnsToProperties(ResultSetMetaData rsmd,
        PropertyDescriptor[] props) throwsSQLException {
 
    intcols = rsmd.getColumnCount();
    int[] columnToProperty = newint[cols + 1];
    Arrays.fill(columnToProperty, PROPERTY_NOT_FOUND);
 
    for(intcol = 1; col <= cols; col++) {
        String columnName = rsmd.getColumnLabel(col);
        if(null== columnName || 0== columnName.length()) {
          columnName = rsmd.getColumnName(col);
        }
        String propertyName = columnToPropertyOverrides.get(columnName);
        if(propertyName == null) {
            propertyName = columnName;
        }
        for(inti = 0; i < props.length; i++) {
 
            if(propertyName.equalsIgnoreCase(props[i].getName())) {
                columnToProperty[col] = i;
                break;
            }
        }
    }
 
    returncolumnToProperty;
}

这里ResultSetMetaDataPropertyDescriptor分别是JDBC和Bean API里获取的元信息。

对字段做类型转换的代码比较多,主要方法是callSetter

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
privatevoid callSetter(Object target, PropertyDescriptor prop, Object value)
        throwsSQLException {
 
    Method setter = prop.getWriteMethod();
 
    if(setter == null) {
        return;
    }
 
    Class<?>[] params = setter.getParameterTypes();
    // convert types for some popular ones
    if(value instanceofjava.util.Date) {
        finalString targetType = params[0].getName();
        if("java.sql.Date".equals(targetType)) {
            value = newjava.sql.Date(((java.util.Date) value).getTime());
        }elseif ("java.sql.Time".equals(targetType)) {
            value = newjava.sql.Time(((java.util.Date) value).getTime());
        }elseif ("java.sql.Timestamp".equals(targetType)) {
            value = newjava.sql.Timestamp(((java.util.Date) value).getTime());
        }
    }
 
    // Don't call setter if the value object isn't the right type
    if(this.isCompatibleType(value, params[0])) {
        setter.invoke(target,newObject[]{value});
    }else{
        thrownew SQLException(
                "Cannot set " + prop.getName() + ": incompatible types, cannot convert "
                        + value.getClass().getName() + " to " + params[0].getName());
        // value cannot be null here because isCompatibleType allows null
    }
 
}

这里省略了一些异常的捕获。this.isCompatibleType方法里有一些关于基本类型和装箱类型的转换。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
privateboolean isCompatibleType(Object value, Class<?> type) {
    // Do object check first, then primitives
    if(value == null|| type.isInstance(value)) {
        returntrue;
 
    }elseif (type.equals(Integer.TYPE) && value instanceofInteger) {
        returntrue;
 
    }elseif (type.equals(Long.TYPE) && value instanceofLong) {
        returntrue;
 
    }elseif (type.equals(Double.TYPE) && value instanceofDouble) {
        returntrue;
 
    }elseif (type.equals(Float.TYPE) && value instanceofFloat) {
        returntrue;
 
    }elseif (type.equals(Short.TYPE) && value instanceofShort) {
        returntrue;
 
    }elseif (type.equals(Byte.TYPE) && value instanceofByte) {
        returntrue;
 
    }elseif (type.equals(Character.TYPE) && value instanceofCharacter) {
        returntrue;
 
    }elseif (type.equals(Boolean.TYPE) && value instanceofBoolean) {
        returntrue;
 
    }
    returnfalse;
 
}

至此,一个ResultSet至Bean的转换就完成了,还是相当简洁的。

0 0
原创粉丝点击