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

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

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

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

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

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

  • 对Bean的字段做类型转换

字段映射的代码:

    protected int[] mapColumnsToProperties(ResultSetMetaData rsmd,
            PropertyDescriptor[] props) throws SQLException {

        int cols = rsmd.getColumnCount();
        int[] columnToProperty = new int[cols + 1];
        Arrays.fill(columnToProperty, PROPERTY_NOT_FOUND);

        for (int col = 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 (int i = 0; i < props.length; i++) {

                if (propertyName.equalsIgnoreCase(props[i].getName())) {
                    columnToProperty[col] = i;
                    break;
                }
            }
        }

        return columnToProperty;
    }

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

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


private void callSetter(Object target, PropertyDescriptor prop, Object value)
        throws SQLException {

    Method setter = prop.getWriteMethod();

    if (setter == null) {
        return;
    }

    Class<?>[] params = setter.getParameterTypes();
    // convert types for some popular ones
    if (value instanceof java.util.Date) {
        final String targetType = params[0].getName();
        if ("java.sql.Date".equals(targetType)) {
            value = new java.sql.Date(((java.util.Date) value).getTime());
        } else if ("java.sql.Time".equals(targetType)) {
            value = new java.sql.Time(((java.util.Date) value).getTime());
        } else if ("java.sql.Timestamp".equals(targetType)) {
            value = new java.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, new Object[]{value});
    } else {
        throw new 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方法里有一些关于基本类型和装箱类型的转换。


private boolean isCompatibleType(Object value, Class<?> type) {
    // Do object check first, then primitives
    if (value == null || type.isInstance(value)) {
        return true;

    } else if (type.equals(Integer.TYPE) && value instanceof Integer) {
        return true;

    } else if (type.equals(Long.TYPE) && value instanceof Long) {
        return true;

    } else if (type.equals(Double.TYPE) && value instanceof Double) {
        return true;

    } else if (type.equals(Float.TYPE) && value instanceof Float) {
        return true;

    } else if (type.equals(Short.TYPE) && value instanceof Short) {
        return true;

    } else if (type.equals(Byte.TYPE) && value instanceof Byte) {
        return true;

    } else if (type.equals(Character.TYPE) && value instanceof Character) {
        return true;

    } else if (type.equals(Boolean.TYPE) && value instanceof Boolean) {
        return true;

    }
    return false;

}

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

你可能感兴趣的:(orm,DbUtils)