dbUtils SQL 查询和行处理器(javaBean行处理器特别篇)

dbUtils SQL 查询和行处理器(javaBean行处理器特别篇)

使用反射+Map实现

大致思路(流程):

  1. 获取并写入实体类的字段集合<注解值(没有注解为字段名),字段>
  2. 得到表的元数据(用于获取总列数和列名)
  3. 接收数据转换成合适的格式并返回
public static <T> T toBean(ResultSet resultSet, Class<T> clazz) throws SQLException {
        /**
        * 获取并写入实体类的字段集合<注解值(没有注解为字段名),字段>
        **/
        // 创建实体类的字段集合
        Map<String, Field> map = new HashMap<>();
        // 遍历实体类的所有字段并存储到Map中
        for (Field field : clazz.getDeclaredFields()) {
            // 打开访问权限(不推荐,后面使用内省)
            field.setAccessible(true);
            // 判断是否有注解
            if (field.isAnnotationPresent(Column.class)){
                // 将<注解值,字段>传入Map中
                map.put(field.getAnnotation(Column.class).value(),field);
            }else{
                // 将<字段名,字段>传入Map中
                map.put(field.getName(), field);
            }
        }

        /**
        * 得到表的元数据(用于获取总列数和列名)
        **/
        ResultSetMetaData metaData = resultSet.getMetaData();

        /**
        * 接收数据转换成合适的格式并返回
        **/
        try {
            // 创建用于接收数据的对象
            T t = clazz.newInstance();
            for (int i = 1; i <= metaData.getColumnCount(); i++) {
                // 获取列名
                String name = metaData.getColumnLabel(i);
                // 判断字段集合是否有对应的字段
                if (map.containsKey(name)) {
                    // 获取字段
                    Field field = map.get(name);
                    // 通过字段写入接收数据的对象
                    field.set(t, resultSet.getObject(i));
                }
            }
            return t;
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }

使用内省

其中为什么会把一些代码封装成方法:

  1. 单一原则
  2. 把可能会发生异常的代码提取到方法中运行,把编译时异常转换成运行时异常

大致思路(流程):

  1. 创建对象

  2. 通过内省获取属性描述器数组

  3. 获取结果集元数据

  4. 遍历结果集列

  5. 获取列名(优先别名)

  6. 遍历属性描述器数组

  7. 根据属性描述器获取字段名称或者注解,并且判断是否和表的列名相符

  8. 通过结果集获取数据并且进行类型转换(如果使用反射就不需要类型转换,但是需要打开权限开关)

  9. 获取并调用set方法进行赋值操作

  10. 返回结果

public class BeanProcessor {

    /**
     * 根据结果集创建Bean实例
     */
    public Object createBean(ResultSet resultSet, Class<?> clazz) throws SQLException {
        // 1. 创建对象
        Object object = newInstance(clazz);
        // 2. 通过内省获取属性描述器数组
        PropertyDescriptor[] propertyDescriptors = propertyDescriptors(clazz);
        // 3. 获取结果集元数据
        ResultSetMetaData metaData = resultSet.getMetaData();
        // 4. 遍历结果集列
        for (int i = 1; i <= metaData.getColumnCount(); i++) {
            // 5. 获取列名(优先别名)
            String columnLabel = metaData.getColumnLabel(i);
            // 6. 遍历属性描述器数组
            for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
                // 7. 根据属性描述器获取字段名称或者注解,并且判断是否和表的列名相符
                if (hasColumnLabel(columnLabel, propertyDescriptor, clazz)) {
                    // 8. 通过结果集获取数据并且进行类型转换
                    Object value = processColumn(propertyDescriptor, columnLabel, resultSet);
                    // 9. 获取并调用set方法进行赋值操作
                    callSetter(propertyDescriptor, object, value);
                }
            }
        }
        // 10. 返回结果
        return object;
    }

    /**
     * 根据Class对象创建实例
     *
     * @param clazz javaBean类型
     * @return javaBean对象
     */
    private Object newInstance(Class<?> clazz) {
        try {
            // 获取构造方法并且创建对象
            return clazz.getConstructor().newInstance();
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 通过内省获取属性描述器数组
     *
     * @param clazz javaBean类型
     * @return 属性描述器数组
     */
    private PropertyDescriptor[] propertyDescriptors(Class<?> clazz) {
        try {
            // Introspector类为工具提供了一种了解目标Java Bean所支持的属性、事件和方法的标准方法。
            // 返回BeanInfo类实例,包含实体的所有信息(方法、属性、事件和其他特性)
            BeanInfo beanInfo = Introspector.getBeanInfo(clazz,Object.class);
            // 返回bean的所有属性的描述符
            return beanInfo.getPropertyDescriptors();
        } catch (IntrospectionException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 根据属性描述器获取字段名称或者注解,并且判断是否和表的列名相符(根据属性描述器解析出表的列名)
     *
     * @param columnLabel        表的列名
     * @param propertyDescriptor 属性描述器:获取字段名称或者注解
     * @param clazz              javaBean类型
     * @return 判断是否和表的列名相符
     */
    private boolean hasColumnLabel(String columnLabel, PropertyDescriptor propertyDescriptor, Class<?> clazz) {
        try {
            // 字段名称
            String fieldName = propertyDescriptor.getName();
            // 通过字段名称获取字段
            Field field = clazz.getDeclaredField(fieldName);
            // 判断是否有对应注解
            if (field.isAnnotationPresent(Column.class)) {
                // 注解的值代替字段名称
                fieldName = field.getAnnotation(Column.class).value();
            }
            // 将此字符串与另一个字符串进行比较,忽略大小写考虑
            return fieldName.equalsIgnoreCase(columnLabel);
        } catch (NoSuchFieldException e) {
//            throw new RuntimeException(e);
            return false;
        }
    }

    /**
     * 通过结果集获取数据并且进行类型转换
     *
     * @param propertyDescriptor 属性描述器:用于获取数据类型进行类型转换
     * @param columnLabel        列名:根据特定的列名获取数据
     * @param resultSet          结果集:用于获取数据
     * @return 返回类型转换后的数据
     */
    private Object processColumn(PropertyDescriptor propertyDescriptor, String columnLabel, ResultSet resultSet) {
        try {
            // 获取数据类型
            Class<?> fieldType = propertyDescriptor.getPropertyType();
            // 根据特定的列名获取数据
            Object value = resultSet.getObject(columnLabel);
            // 如果字段不是基本数据类型,同时数据为空(因为基本数据类型不允许为空,如果传入数据为空会报错)
            if (!fieldType.isPrimitive() && value == null) {
                return null;
            }
            // 类型转换链
            return BeanUtils.toBean(propertyDescriptor,value);
//            return null;
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 获取并调用set方法进行赋值操作
     *
     * @param propertyDescriptor 属性描述器:用于获取set方法进行赋值操作
     * @param object             需要赋值的对象
     * @param value              赋值的值
     */
    private void callSetter(PropertyDescriptor propertyDescriptor, Object object, Object value) {
        try {
            // 获取并调用set方法进行赋值操作
            propertyDescriptor.getWriteMethod().invoke(object, value);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        }
    }
}

类型转换链