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; }
這裏ResultSetMetaData
和PropertyDescriptor
分別是JDBC和Bean API裏獲取的元信息。code
對字段作類型轉換的代碼比較多,主要方法是callSetter
。ip
<!-- 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的轉換就完成了,仍是至關簡潔的。