DbUtils源码分析-BeanProcessor

在BasicRowProcessor的toBean/toBeanList方法中,没有在方法中直接处理,而是交给BeanProcessor去处理,BeanProcessor主要是利用反射实例化对象,获取属性描述器PropertyDescriptor,调用setter方法,然后返回对象。

1.populateBean 比较关键的一个方法,用于处理属性赋值

 private <T> T populateBean(ResultSet rs, T bean, PropertyDescriptor[] props, 
        int[]  columnToProperty) throws SQLException {
        /*
        由于rs获取的字段顺序和PropertyDescriptor不同,需要columnToProperty来表示他们间的对应关系
        如果查询没有对应的属性,则对象的columnToProperty上的值为-1,且数组下标从1开始
        */
        for (int i = 1; i < columnToProperty.length; i++) {
            //PROPERTY_NOT_FOUND=-1,表示没有和当前ResultSet对应的的字段匹配的属性
            if (columnToProperty[i] == PROPERTY_NOT_FOUND) {
                continue;
            }
            //获取属性描述器和当前ResultSet.getObject(i)对应
            PropertyDescriptor prop = props[columnToProperty[i]];
            //获取属性类型
            Class<?> propType = prop.getPropertyType();
            Object value = null;
            if(propType != null) {
                //获取相应的值
                value = this.processColumn(rs, i, propType);
                /*
                这里会对8种基本数据类型的数据处理,如果这8种基本数据类型查询结果为NUL,则根据primitiveDefaults取相应的默认值
                不是原始类型则不处理,即为NULL(基本数据类型:boolean、char、byte、short、int、long、float、double)
                */
                if (value == null && propType.isPrimitive()) {
                    //取默认值
                    value = primitiveDefaults.get(propType);
                }
            }
            //调用setter方法赋值
            this.callSetter(bean, prop, value);
        }
        //set完值后返回对象
        return bean;
    }

2.再看一个上面用到的方法processColumn

protected Object processColumn(ResultSet rs, int index, Class<?> propType) throws SQLException {
        //先获取ResultSet对应的值
        Object retval = rs.getObject(index);
        //如果查询的值为NULL,且不是基本数据类型则返回NULL
        if (!propType.isPrimitive() && retval == null ) {
            return null;
        }
        /*
        dbutils提供ColumnHandler的8种基本数据类型子类(char对应为String),是针对ResultSet结果中的对象进行封装,
        还有两个SQLXMLColumnHandler/TimestampColumnHandler
        ServiceLoader<ColumnHandler>第一次接触这个东西,它可以保存相应的classpath下ColumnHandler的实现类。
        这里用于迭代判断类型是否符合
        */
        for (ColumnHandler handler : columnHandlers) {
            if (handler.match(propType)) {
                //类型匹配取相应的值,结束循环,内部只是做一个get操作,然后类型转化一下
                retval = handler.apply(rs, index);
                break;
            }
        }
        return retval;
    }

3.callSetter 属性赋值,调用setter方法

 //获取setter方法
 protected Method getWriteMethod(Object target, PropertyDescriptor prop, Object value) {
        Method method = prop.getWriteMethod();
        return method;
 }

private void callSetter(Object target, PropertyDescriptor prop, Object value) throws SQLException {
        //只要一个PropertyDescriptor 参数就可以啊?不理解为什么这样写
        Method setter = getWriteMethod(target, prop, value);
        //如果不存在setter方法,或者setter方法参数不是一个的话,直接结束,不是没提供setter方法,就是 不是标准的setter方法
        if (setter == null || setter.getParameterTypes().length != 1) {
            return;
        }
        try {
            Class<?> firstParam = setter.getParameterTypes()[0];
            //针对Bean中的属性进行封装PropertyHandler,主要对时间日期的转化(DatePropertyHandler)
            for (PropertyHandler handler : propertyHandlers) {
                if (handler.match(firstParam, value)) {
                    value = handler.apply(firstParam, value);
                    break;
                }
            }
            //类型检查,检查value是否能转成firstParam的类型
            if (this.isCompatibleType(value, firstParam)) {
                setter.invoke(target, new Object[]{value});
            } else {
              throw new SQLException(
                  "Cannot set " + prop.getName() + ": incompatible types, cannot convert "
                  + value.getClass().getName() + " to " + firstParam.getName());
            }
        } 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());
        }
    }

前面一些方法算是BeanProcessor最底层的处理了,其他方法都基于这些方法上直接或间接调用

4.<T> T populateBean(ResultSet rs, T bean)

 protected int[] mapColumnsToProperties(ResultSetMetaData rsmd, PropertyDescriptor[] props) throws SQLException {
     int cols = rsmd.getColumnCount();
     //下标从1开始
     int[] columnToProperty = new int[cols + 1];
     //初始化-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++) {
             //设置和ResultSet字段匹配的对应的数组下标
             if (propertyName.equalsIgnoreCase(props[i].getName())) {
                 columnToProperty[col] = i;
                 break;
             }
         }
     }
     return columnToProperty;
 }

private PropertyDescriptor[] propertyDescriptors(Class<?> c)
        throws SQLException {
        // Introspector caches BeanInfo classes for better performance
        BeanInfo beanInfo = null;
        try {
            beanInfo = Introspector.getBeanInfo(c);

        } catch (IntrospectionException e) {
            throw new SQLException(
                "Bean introspection failed: " + e.getMessage());
        }
        return beanInfo.getPropertyDescriptors();
    }

public <T> T populateBean(ResultSet rs, T bean) throws SQLException {
        //根据bean获取属性描述器
        PropertyDescriptor[] props = this.propertyDescriptors(bean.getClass());
        //获取元数据
        ResultSetMetaData rsmd = rs.getMetaData();
        //因为ResultSet和PropertyDescriptor的属性不一定一一对应,所以需要一个数组标识他们间的对应关系
        //如果在构造函数中提供自定义的属性名称,优先级(自定义属性名称>SQL别名>数据库中字段名)
        int[] columnToProperty = this.mapColumnsToProperties(rsmd, props);
        //调用私有populateBean即可
        return populateBean(rs, bean, props, columnToProperty);
}

5.对外开放的主要两个方法toBean和toBeanList

//只做实例化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());
    }
}

//实例化并初始化bean
 private <T> T createBean(ResultSet rs, Class<T> type,  PropertyDescriptor[] props, int[] columnToProperty)
    throws SQLException {

        T bean = this.newInstance(type);
        return populateBean(rs, bean, props, columnToProperty);
 }

public <T> T toBean(ResultSet rs, Class<? extends T> type) throws SQLException {
        //实例化空对象
        T bean = this.newInstance(type);
        //初始化bean,赋值
        return this.populateBean(rs, bean);
}

public <T> List<T> toBeanList(ResultSet rs, Class<? extends T> type) throws SQLException {
      List<T> results = new ArrayList<T>();
       //没有记录直接返回空列表
       if (!rs.next()) {
           return results;
       }
       PropertyDescriptor[] props = this.propertyDescriptors(type);
       ResultSetMetaData rsmd = rs.getMetaData();
       int[] columnToProperty = this.mapColumnsToProperties(rsmd, props);
       do {
           //直接调用添加即可,再跑个循环结束
           results.add(this.createBean(rs, type, props, columnToProperty));
       } while (rs.next());
       return results;
}

6.剩下的就是构造函数了,和一些静态字段

public BeanProcessor() {
        this(new HashMap<String, String>());
}

 //提供自定义的属性和查询字段的对应关系Map,不提供则按Bean属性名和查询字段匹配对应
 public BeanProcessor(Map<String, String> columnToPropertyOverrides) {
    super();
    if (columnToPropertyOverrides == null) {
        throw new IllegalArgumentException("columnToPropertyOverrides map cannot be null");
    }
    this.columnToPropertyOverrides = columnToPropertyOverrides;
}

//静态代码块中初始化 基本数据类型为空时的默认值
private static final Map<Class<?>, Object> primitiveDefaults = new HashMap<Class<?>, Object>();
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));
}

//用于加载ColumnHandler的实现类,判断数据库中查询结果的类型
private static final ServiceLoader<ColumnHandler> columnHandlers = ServiceLoader.load(ColumnHandler.class);

//用于加载PropertyHandler的实现类,判断Bean属性的类型
private static final ServiceLoader<PropertyHandler> propertyHandlers = ServiceLoader.load(PropertyHandler.class);

//Bean属性和ResultSet字段对应Map
private final Map<String, String> columnToPropertyOverrides;
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值