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; }}
阅读全文
0 0
- DBUtils学习----BeanProcessor类
- DbUtils学习----DbUtils类
- BeanProcessor
- DBUtils – BeanProcessor扩展,支持自定义字段映射
- dbutils 重写BeanProcessor 部分方法,支持enum的转换
- DBUtils – BeanProcessor扩展,支持自定义字段映射
- DBUtils – BeanProcessor扩展,支持自定义字段映射
- DBUtils学习----QueryRunner类
- DBUtils学习----QueryLoader类
- DbUtils学习
- DBUtils学习
- DBUtils学习
- DBUtils学习笔记一DbUtils
- DbUtils 扩展BeanProcessor的处理方式,使其能够处理如DATA_OBJECT_NAME -> dataObjectName这样的映射关系
- 零基础学习DBUtils--DBUtils基础知识小结
- apache commons DbUtils学习
- DBUtils学习(一)
- DBUtils学习(二)
- maven 创建多模块备忘(1)之搭建结构
- MySQL执行计划 EXPLAIN查询解释
- 网络管理(一)
- 1249. 三角形
- 《哞知》——我的第一个软件
- DBUtils学习----BeanProcessor类
- ES6 — Symbol
- 17muduo_base库源码分析(八)
- 不愿看到Java开发者再做的10件事
- OpenJudge Pell数列
- 题目1038:Sum of Factorials
- poj3241 曼哈顿最小生成树
- Friend-Graph
- Intellij IDEA创建maven项目无java文件问题