Commons DbUtis源码阅读四

来源:互联网 发布:冷库负荷计算软件 编辑:程序博客网 时间:2024/05/15 02:22

  在读BasicRowProcessor类时,它有两个方法(toBean和toBeanList)都将最终的处理工作交给了BeanProcessor,所以,今天来拜读一下此类, 在读此类的时候,决定换个方式,就一个方法慢慢的展开分析,这样或许也会更有趣味和吸引力吧:

 

Java代码  收藏代码
  1. public <T> T toBean(ResultSet rs, Class<T> type) throws SQLException {  
  2.   
  3.     PropertyDescriptor[] props = this.propertyDescriptors(type);// 1)获取指定类的属性描述对象集合  
  4.   
  5.     ResultSetMetaData rsmd = rs.getMetaData();  
  6.     int[] columnToProperty = this.mapColumnsToProperties(rsmd, props);//2)列名转换为bean属性  
  7.   
  8.     return this.createBean(rs, type, props, columnToProperty);//3)  
  9. }  

 

  这个方法呢,是将ResultSet的指定行创建一个指定类型的Bean实例返回:

  1)这个是利用内省机制,获取指定类的属性描述数组,来看看这个propertyDescriptors方法

 

Java代码  收藏代码
  1. private PropertyDescriptor[] propertyDescriptors(Class<?> c)  
  2.         throws SQLException {  
  3.     // Introspector caches BeanInfo classes for better performance  
  4.     BeanInfo beanInfo = null;  
  5.     try {  
  6.         beanInfo = Introspector.getBeanInfo(c);  
  7.   
  8.     } catch (IntrospectionException e) {  
  9.         throw new SQLException("Bean introspection failed: "  
  10.                 + e.getMessage());  
  11.     }  
  12.   
  13.     return beanInfo.getPropertyDescriptors();  
  14. }  

   通过Introspector的静态方法getBeanInfo,获取指定类型的BeanInfo实例,再调用getPropertyDescriptors()方法返回该类的所有PropertyDescriptor数组,非常的简洁明了,这个就是传说中的内省了

  2)这个方法的功能呢,是将column数组与类的properties匹配,两个都是数组,然后返回还是个数组,居然还map名,所以,看看里面是如何实现的:

 

Java代码  收藏代码
  1. protected int[] mapColumnsToProperties(ResultSetMetaData rsmd,  
  2.         PropertyDescriptor[] props) throws SQLException {  
  3.   
  4.     int cols = rsmd.getColumnCount();  
  5.     int columnToProperty[] = new int[cols + 1];  
  6.     Arrays.fill(columnToProperty, PROPERTY_NOT_FOUND);  
  7.   
  8.     for (int col = 1; col <= cols; col++) {  
  9.         String columnName = rsmd.getColumnLabel(col);//获取列名  
  10.         if (null == columnName || 0 == columnName.length()) {  
  11.             columnName = rsmd.getColumnName(col);  
  12.         }  
  13.         for (int i = 0; i < props.length; i++) {  
  14.   
  15.             if (columnName.equalsIgnoreCase(props[i].getName())) {//与属性名进行匹配                    columnToProperty[col] = i;  
  16.                 break;  
  17.             }  
  18.         }  
  19.     }  
  20.     // comlumnToProperty 里面返回的是column下标对应的PropertyDescriptor属性的下标值  
  21.   
  22.     return columnToProperty;  
  23. }  

   这个方法的所做的工作实际上是这样的,将ResultSet中的列名与Bean的属性进行匹配,声明的数组的长度是以ResultSet中的列数量来确定的,所以下标从1开始,然后与Bean的PropertyDescriptor数组进行属性名匹配,如果有找到,则在对应的列名下标数组里存入bean属性的位置。这样一来,我们就可以清晰的知道,ResultSet列名对应的Bean属性了。

  OK,也许你现在看这段话有些稀里糊涂,你只要记住,这个返回的数组是按照ResultSet的列标匹配bean属性数组对应的位置的。

  3)看关键方法

 

Java代码  收藏代码
  1. private <T> T createBean(ResultSet rs, Class<T> type,  
  2.         PropertyDescriptor[] props, int[] columnToProperty)  
  3.         throws SQLException {  
  4.   
  5.     T bean = this.newInstance(type);//创建该类实例  
  6.     //循环列名与bean属性匹配情况数组  
  7.     for (int i = 1; i < columnToProperty.length; i++) {  
  8.   
  9.         if (columnToProperty[i] == PROPERTY_NOT_FOUND) {//该下标没有匹配的属性,则不做处理  
  10.             continue;  
  11.         }  
  12.   
  13.         PropertyDescriptor prop = props[columnToProperty[i]];//获取对应属性的描述对象  
  14.         Class<?> propType = prop.getPropertyType();//获取类型  
  15.   
  16.         Object value = this.processColumn(rs, i, propType);//获取rs对应的值  
  17.   
  18.         if (propType != null && value == null && propType.isPrimitive()) {  
  19.             value = primitiveDefaults.get(propType);//为空,获取默认值  
  20.         }  
  21.   
  22.         this.callSetter(bean, prop, value);//调用属性的setter方法  
  23.     }  
  24.   
  25.     return bean;  
  26. }  

   这个方法呢,首先来生成一个指定类型的实例,然后循环之前处理ResultSet列名与Bean属性名匹配情况的数组,获取Bean属性特定的PropertyDescriptor实例,通过this.processColumn(rs, i, propType)这个方法获取数据库中对应的值,再调用callSetter(...)方法来为新建Bean实例赋值上值,来看看它是怎么实现的:

 

Java代码  收藏代码
  1. Method setter = prop.getWriteMethod();//通过PropertyDescriptor获取对应属性的Setter方法  

 

 然后匹配数据库类型与Bean方法的类型,如果不匹配,则抛出异常,否则通过反射来为Bean对象填入对应的值,来看具体实现:

 

Java代码  收藏代码
  1. Class<?>[] params = setter.getParameterTypes();//参数类型列表  
  2.  ............................................................................  
  3.   
  4. // Don't call setter if the value object isn't the right type   
  5.   if (this.isCompatibleType(value, params[0])) {  
  6.                 setter.invoke(target, new Object[] { value });  
  7.  } else {  
  8.               throw new SQLException(  
  9.                   "Cannot set " + prop.getName() + ": incompatible types.");  
  10.  }  

 我们深刻的分析了一下toBean方法,toBeanList方法呢,实际上就是循环ResultSet,获取每一行,然后交由toBean()方法处理,贴出具体的代码:

 

Java代码  收藏代码
  1. public <T> List<T> toBeanList(ResultSet rs, Class<T> type) throws SQLException {  
  2.     List<T> results = new ArrayList<T>();  
  3.   
  4.     if (!rs.next()) {  
  5.         return results;  
  6.     }  
  7.             
  8.     PropertyDescriptor[] props = this.propertyDescriptors(type);  
  9.     ResultSetMetaData rsmd = rs.getMetaData();  
  10.     int[] columnToProperty = this.mapColumnsToProperties(rsmd, props);  
  11.      
  12.     do {  
  13.         results.add(this.createBean(rs, type, props, columnToProperty));  
  14.     } while (rs.next());  
  15.   
  16.     return results;  
  17. }  

     这个方法,我没有搞明白为什么不直接循环调用toBean()方法,直接调用toBean()方法不好嘛?!(或许是我资力尚浅,没有明白作者的意图)

    这里也贴出个人认为可以完善的这个方法实现吧

 

Java代码  收藏代码
  1. //        PropertyDescriptor[] props = this.propertyDescriptors(type);  
  2. //        ResultSetMetaData rsmd = rs.getMetaData();  
  3. //        int[] columnToProperty = this.mapColumnsToProperties(rsmd, props);  
  4.   
  5.         do {  
  6. //            results.add(this.createBean(rs, type, props, columnToProperty));  
  7.             results.add(this.toBean(rs, type));//修改部分  
  8.         } while (rs.next());  

  如果有朋友认为我这样写不对,还请不吝教导。

0 0
原创粉丝点击