使用反射+Map实现
大致思路(流程):
- 获取并写入实体类的字段集合<注解值(没有注解为字段名),字段>
- 得到表的元数据(用于获取总列数和列名)
- 接收数据转换成合适的格式并返回
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);
}
}
使用内省
其中为什么会把一些代码封装成方法:
- 单一原则
- 把可能会发生异常的代码提取到方法中运行,把编译时异常转换成运行时异常
大致思路(流程):
-
创建对象
-
通过内省获取属性描述器数组
-
获取结果集元数据
-
遍历结果集列
-
获取列名(优先别名)
-
遍历属性描述器数组
-
根据属性描述器获取字段名称或者注解,并且判断是否和表的列名相符
-
通过结果集获取数据并且进行类型转换(如果使用反射就不需要类型转换,但是需要打开权限开关)
-
获取并调用set方法进行赋值操作
-
返回结果
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);
}
}
}