小酒一杯品源碼-DbUtils代碼解讀

ORM一直是Web開發一個熱點話題,DbUtils則是給出了一個至關簡潔的答案。DbUtils的嵌套也不深,並且主動的API調用也很是符合程序員的思惟(Hibernate和iBatis這種隱藏了大多數細節的框架,連找到個入口都要費半天勁)。java

話說最經常使用的CRUD,使用JDBC最痛的無非是將ResultSet轉換爲JavaBean。DbUtils則是正好命中這個要害,使用ResultSetHandler機制來解決這個問題。程序員

以前用過Spring JDBC Template,差很少也是這個機制。DbUtils的亮點則是BeanHandler,能夠無需手寫轉換函數,自動根據class生成一個handler。sql

BeanProcessor的核心代碼作了幾件事:框架

  • 提取Bean的字段信息,結果集的字段信息,並做映射;ide

  • 對Bean的字段作類型轉換函數

字段映射的代碼:this

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裏獲取的元信息。code

對字段作類型轉換的代碼比較多,主要方法是callSetterip

<!-- lang: java -->
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方法裏有一些關於基本類型和裝箱類型的轉換。開發

<!-- lang: java -->
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的轉換就完成了,仍是至關簡潔的。

相關文章
相關標籤/搜索