DBUtils学习----BeanProcessor类

来源:互联网 发布:软件维护工程师 编辑:程序博客网 时间:2024/06/05 05:20

作用:

匹配列名到Bean属性名,并转换结果集列到Bean对象的属性中

解析:

package org.apache.commons.dbutils;import java.beans.BeanInfo;import java.beans.IntrospectionException;import java.beans.Introspector;import java.beans.PropertyDescriptor;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;import java.sql.ResultSet;import java.sql.ResultSetMetaData;import java.sql.SQLException;import java.sql.SQLXML;import java.sql.Timestamp;import java.util.ArrayList;import java.util.Arrays;import java.util.HashMap;import java.util.List;import java.util.Map;/** * BeanProcessor 类将列名和JavaBean属性名匹配,并将 ResultSet 列的值转换为 JavaBean 的属性的值 * 子类应该覆盖此类方法以定制行为 * 这个类是线程安全的 */public class BeanProcessor {    /**     * 由 mapColumnsToProperties 方法使用的特殊的数组的值     * 该值表示没有 JavaBean 属性和 ResultSet 列的值对应     */    protected static final int PROPERTY_NOT_FOUND = -1;    /**     * 当返回SQL NULL 时,将 JavaBean 的原始属性设置为这些默认值     * 这些默认值和 ResultSet 的getXXX() 方法在空列时返回的值相同     */    private static final Map<Class<?>, Object> primitiveDefaults = new HashMap<Class<?>, Object>();    /**     * ResultSet 列到 JavaBean 属性的映射     * columnName:propertyName     */    private final Map<String, String> columnToPropertyOverrides;    //设置默认值    static {        primitiveDefaults.put(Integer.TYPE, Integer.valueOf(0));        primitiveDefaults.put(Short.TYPE, Short.valueOf((short) 0));        primitiveDefaults.put(Byte.TYPE, Byte.valueOf((byte) 0));        primitiveDefaults.put(Float.TYPE, Float.valueOf(0f));        primitiveDefaults.put(Double.TYPE, Double.valueOf(0d));        primitiveDefaults.put(Long.TYPE, Long.valueOf(0L));        primitiveDefaults.put(Boolean.TYPE, Boolean.FALSE);        primitiveDefaults.put(Character.TYPE, Character.valueOf((char) 0));    }    /**     * BeanProcessor 的构造函数     */    public BeanProcessor() {        this(new HashMap<String, String>());    }    /**     * BeanProcessor 的构造函数     * columnToPropertyOverrides:列:属性     */    public BeanProcessor(Map<String, String> columnToPropertyOverrides) {        super();        if (columnToPropertyOverrides == null) {            throw new IllegalArgumentException("columnToPropertyOverrides map cannot be null");        }        this.columnToPropertyOverrides = columnToPropertyOverrides;    }    /**     * 将 ResultSet 的一行转换为一个 JavaBean     * 该实现利用反射和 BeanInfo 类来将列名和 JavaBean 属性名匹配     * 该匹配基于如下几个因素:     * 1、     *     该类具有一个可写属性,其名称与列相同     *     名称比较是不区分大小写的     * 2、     *     可以使用 ResultSet 的 getXXX() 方法得到的列类型转换为属性的setXXX方法参数的类型     *     如果转换失败(如:属性为 int 类型,列为 Timestamp 类型),将抛出 SQLException 异常     *     * 当从 ResultSet 返回SQL NULL时,将原始 JavaBean 属性设置为默认值     * 数字字段设置为0,而布尔值设置为false     * 当返回SQL null时,Object 属性被设置为null     * 这与ResultSet 的 getXXX() 方法表现相同     * <T>:想要创建的 JavaBean 的类型     * rs:提供 JavaBean 数据的 ResultSet 类     * type: 创建 JavaBean 实例的类     */    public <T> T toBean(ResultSet rs, Class<T> type) throws SQLException {        //获得 type 的属性描述符 PropertyDescriptor[]        //类似于:java.beans.PropertyDescriptor[name=age; propertyType=int; readMethod=public int com.xiya.entity.Person.getAge(); writeMethod=public void com.xiya.entity.Person.setAge(int)]        PropertyDescriptor[] props = this.propertyDescriptors(type);        //获得 ResultSet 的元信息        ResultSetMetaData rsmd = rs.getMetaData();        //获得 列 到 属性 的映射信息(也就是某一列对应于哪一个PropertyDescriptor)        int[] columnToProperty = this.mapColumnsToProperties(rsmd, props);        return this.createBean(rs, type, props, columnToProperty);    }    /**     * 将 ResultSet 转换为 List<JavaBean>     */    public <T> List<T> toBeanList(ResultSet rs, Class<T> type) throws SQLException {        List<T> results = new ArrayList<T>();        //如果 ResultSet 结果集为空,则返回空集合        if (!rs.next()) {            return results;        }        //见上        PropertyDescriptor[] props = this.propertyDescriptors(type);        ResultSetMetaData rsmd = rs.getMetaData();        int[] columnToProperty = this.mapColumnsToProperties(rsmd, props);        //将 JavaBean 添加进集合        do {            results.add(this.createBean(rs, type, props, columnToProperty));        } while (rs.next());        return results;    }    /**     * 创建一个新对象,并从 ResultSet 中获取字段数据用来初始化该对象     * <T>:想要创建的 JavaBean 的类型     * rs:ResultSet     * type:返回的对象的类型     * props:属性描述符     * columnToProperty:结果集中的列索引     * 返回初始化后的对象     * 数据库访问出错将抛出 SQLException 异常     */    private <T> T createBean(ResultSet rs, Class<T> type,            PropertyDescriptor[] props, int[] columnToProperty)            throws SQLException {        T bean = this.newInstance(type);        for (int i = 1; i < columnToProperty.length; i++) {            if (columnToProperty[i] == PROPERTY_NOT_FOUND) {                continue;            }            // 获得属性描述符            // 类似于:java.beans.PropertyDescriptor[name=age; propertyType=int; readMethod=public int com.xiya.entity.Person.getAge();             //                                       writeMethod=public void com.xiya.entity.Person.setAge(int)]            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);                }            }            //调用setter方法            this.callSetter(bean, prop, value);        }        return bean;    }    /**     * 调用给定的setter方法     * 如果属性不存在setter方法,则此方法不执行任何操作     */    private void callSetter(Object target, PropertyDescriptor prop, Object value)            throws SQLException {        Method setter = prop.getWriteMethod();        if (setter == null) {            return;        }        Class<?>[] params = setter.getParameterTypes();        try {            // 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)) {                    Timestamp tsValue = (Timestamp) value;                    int nanos = tsValue.getNanos();                    value = new java.sql.Timestamp(tsValue.getTime());                    ((Timestamp) value).setNanos(nanos);                }            } else if (value instanceof String && params[0].isEnum()) {                value = Enum.valueOf(params[0].asSubclass(Enum.class), (String) value);            }            // 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            }        } catch (IllegalArgumentException e) {            throw new SQLException(                "Cannot set " + prop.getName() + ": " + e.getMessage());        } catch (IllegalAccessException e) {            throw new SQLException(                "Cannot set " + prop.getName() + ": " + e.getMessage());        } catch (InvocationTargetException e) {            throw new SQLException(                "Cannot set " + prop.getName() + ": " + e.getMessage());        }    }    /**     * ResultSet 的 getObject() 对于 INT 类型的列返回 Integer 类型的对象     * JavaBean属性的setter方法可以使用 Integer 或者 int 类型的参数      *      * 如果 value 值可以成功地传递到setter方法,则返回 true     * Method.invoke() 将 Integer 解包处理成 int     *     * value:将被传递到setter方法的值     * type:setter的参数类型(非空)     */    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;    }    /**     * 返回给定 Class 的新实例的工厂方法     * 这在bean创建过程的开始处被调用,并且可以被覆盖,以提供类似返回缓存bean实例的自定义行为     */    protected <T> T newInstance(Class<T> c) throws SQLException {        try {            return c.newInstance();        } catch (InstantiationException e) {            throw new SQLException(                "Cannot create " + c.getName() + ": " + e.getMessage());        } catch (IllegalAccessException e) {            throw new SQLException(                "Cannot create " + c.getName() + ": " + e.getMessage());        }    }    /**     * 对于给定的Class 返回 PropertyDescriptor[]     */    private PropertyDescriptor[] propertyDescriptors(Class<?> c)        throws SQLException {        // Introspector 缓存 BeanInfo 以获得更好的性能        BeanInfo beanInfo = null;        try {            beanInfo = Introspector.getBeanInfo(c);        } catch (IntrospectionException e) {            throw new SQLException(                "Bean introspection failed: " + e.getMessage());        }        return beanInfo.getPropertyDescriptors();    }    /**     * 返回的数组中的索引下标表示的是列的编号     * 在数组中每个位置存储的值表示与列名相匹配的bean属性在 PropertyDescriptor[] 中的位置索引     * 如果在列中没有发现bean属性,则将该位置设置为PROPERTY_NOT_FOUND     *      * 返回一个int[],表示列索引到属性索引的映射     * 位置在[0]的元素是没有意义的,因为JDBC 列索引从1开始     */    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())) {                    //当前列对应 PropertyDescriptor[] 的位置                    columnToProperty[col] = i;                    break;                }            }        }        return columnToProperty;    }    /**     * 将 ResultSet 列转换为对象     * 简单的实现可以调用 rs.getObject(index)     * 复杂的实现可以先执行类型操作,将列的类型和bean属性的类型匹配     *     * 对于给定的属性类型,这个实现调用适当ResultSet getter方法来执行类型转换     * 如果属性类型和 ResultSet 所支持的类型不匹配,则调用getObject()     *     * rs:当前处理的 ResultSet,在传递给该方法之前,它必须指向一个有效的行     * index: 当前处理的列的索引     * propType:列需要转换成的bean的属性的类型     * 数据库访问出错将抛出 SQLException 异常     * 返回给定列索引的对象,如果列值为空则返回null     */    protected Object processColumn(ResultSet rs, int index, Class<?> propType)        throws SQLException {        //判断propType是不是原始类型(int/double/byte/void等9个类型)        if ( !propType.isPrimitive() && rs.getObject(index) == null ) {            return null;        }        if (propType.equals(String.class)) {            return rs.getString(index);        } else if (            propType.equals(Integer.TYPE) || propType.equals(Integer.class)) {            return Integer.valueOf(rs.getInt(index));        } else if (            propType.equals(Boolean.TYPE) || propType.equals(Boolean.class)) {            return Boolean.valueOf(rs.getBoolean(index));        } else if (propType.equals(Long.TYPE) || propType.equals(Long.class)) {            return Long.valueOf(rs.getLong(index));        } else if (            propType.equals(Double.TYPE) || propType.equals(Double.class)) {            return Double.valueOf(rs.getDouble(index));        } else if (            propType.equals(Float.TYPE) || propType.equals(Float.class)) {            return Float.valueOf(rs.getFloat(index));        } else if (            propType.equals(Short.TYPE) || propType.equals(Short.class)) {            return Short.valueOf(rs.getShort(index));        } else if (propType.equals(Byte.TYPE) || propType.equals(Byte.class)) {            return Byte.valueOf(rs.getByte(index));        } else if (propType.equals(Timestamp.class)) {            return rs.getTimestamp(index);        } else if (propType.equals(SQLXML.class)) {            return rs.getSQLXML(index);        } else {            return rs.getObject(index);        }    }}

BeanProcessor 存在一个子类GenerousBeanProcessor ,重写了父类的mapColumnsToProperties 方法。

package org.apache.commons.dbutils;import java.beans.PropertyDescriptor;import java.sql.ResultSetMetaData;import java.sql.SQLException;import java.util.Arrays;/** * 提供数据库列到JavaBean属性的名称匹配 */public class GenerousBeanProcessor extends BeanProcessor {    /**     * 默认构造函数     */    public GenerousBeanProcessor() {        super();    }    @Override    protected int[] mapColumnsToProperties(final ResultSetMetaData rsmd,            final PropertyDescriptor[] props) throws SQLException {        final int cols = rsmd.getColumnCount();        final 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);            }            final String generousColumnName = columnName.replace("_", "");            for (int i = 0; i < props.length; i++) {                final String propName = props[i].getName();                // see if either the column name, or the generous one matches                if (columnName.equalsIgnoreCase(propName) ||                        generousColumnName.equalsIgnoreCase(propName)) {                    columnToProperty[col] = i;                    break;                }            }        }        return columnToProperty;    }}
原创粉丝点击