Jdbc 比较繁琐的一个操作就是解析结果集ResultSet, 在实际开发时, 通常会将对结果集的解析封装为一个工具类. 需要注意的时, jdbc查询出来的属性可能不能直接转换为java的类型, 比如说java.sql.Date, 不能直接转换为java.util.Date 或LocalDate等类型, 需要自定义转换器. 如果比较熟悉Mybatis的话, 会发现Mybatis底层也封装了大量的类型转换器.

  1. 工具类源码

  笔者的工具类比较简单, 只封装了三个方法:

  方法签名  方法描述  参数说明

  public static LinkedHashMap toPropertyMap(ResultSet resultSet) throws SQLException  转换单行结果集为Map结构. key为列别名, value为列值  resultSet: 查询结果集

  public static T toBean(ResultSet resultSet, Class clz)  转换结果集为单行对象  clz: 目标对象类型

  resultSet: 结果集

  public static List toBeans(ResultSet resultSet, Class clz) throws SQLException  转换结果集为java对象集合.  clz: 要转换的javaBean类

  resultSet: 结果集

  1.1 ResultSetUtil 源码

/** 结果集解析工具类
  * @since 1.0
  * @author zongf
  * @created 2019-07-18
  */
  public class ResultSetUtil {
  /** 转换单行结果集为Map结构. key为列别名, value为列值
  * @param resultSet 查询结果集
  * @return 结果集中为空时, 返回null
  * @since 1.0
  * @author zongf
  * @created 2019-07-18
  */
  public static LinkedHashMap toPropertyMap(ResultSet resultSet) throws SQLException {
  // 如果结果集为空,则返回null
  if (!resultSet.next()) return null;
  LinkedHashMap cloumnMap = new LinkedHashMap<>();
  // 获取结果集元信息
  ResultSetMetaData metaData = resultSet.getMetaData();
  // 获取每一列列名与值
  for (int i = 1; i <= metaData.getColumnCount(); i++) {
  // 获取列别名为key
  String columnLabel = metaData.getColumnLabel(i);
  Object columnValue = resultSet.getObject(i);
  cloumnMap.put(columnLabel, columnValue);
  }
  return cloumnMap;
  }
  /** 转换结果集为单行对象
  * @param clz 目标对象类型
  * @param resultSet 结果集
  * @since 1.0
  * @return null
  * @author zongf
  * @created 2019-07-18
  */
  public static T toBean(ResultSet resultSet, Class clz) {
  try {
  LinkedHashMap propertyMap = toPropertyMap(resultSet);
  return ReflectUtil.newInstance(clz, propertyMap, new DateTypeConverter());
  } catch (SQLException e) {
  throw new RuntimeException("sql 执行异常!", e);
  }
  }
  /** 转换结果集为java对象集合.
  * @param clz 要转换的javaBean类
  * @param resultSet 结果集
  * @return 结果集中没有数据时, 返回null
  * @since 1.0
  * @author zongf
  * @created 2019-07-18
  */
  public static List toBeans(ResultSet resultSet, Class clz) throws SQLException {
  List list = new ArrayList<>();
  LinkedHashMap propertyMap = null;
  // 解析结果集
  while ((propertyMap = toPropertyMap(resultSet)) != null) {
  T t = (T) ReflectUtil.newInstance(clz, propertyMap, new DateTypeConverter());
  if(t != null) list.add(t);
  }
  return list.size() > 0 ? list : null;
  }
  }

  1.2 ReflectUtil 源码

  这是笔者对使用到的反射技术封装的一个简单工具类.

/** 反射工具类
  * @since 1.0
  * @author zongf
  * @created 2019-07-18
  */
  public class ReflectUtil {
  /**为对象属性赋值
  * @param target 目标对象
  * @param property 属性名
  * @param property 属性名
  * @return value 属性值
  * @since 1.0
  * @author zongf
  * @created 2019-07-18
  */
  public static void setPropertyValue(Object target, String property, Object value) {
  try {
  PropertyDescriptor descriptor = new PropertyDescriptor(property, target.getClass());
  Method writeMethod = descriptor.getWriteMethod();
  writeMethod.invoke(target, value);
  } catch (Exception e) {
  throw new RuntimeException("为对象属性赋值异常!",e);
  }
  }
  /** 获取对象属性值
  * @param target 目标对象
  * @param property 属性
  * @return Object 返回对象属性值
  * @since 1.0
  * @author zongf
  * @created 2019-07-18
  */
  public static Object getPropertyValue(Object target, String property) {
  try {
  PropertyDescriptor descriptor = new PropertyDescriptor(property, target.getClass());
  Method readMethod = descriptor.getReadMethod();
  return readMethod.invoke(target);
  } catch (Exception e) {
  throw new RuntimeException("获取对象属性异常!",e);
  }
  }
  /** 反射创建对象
  * @param clz 目标对象的类型
  * @return propertiesMap 目标对象的属性与值
  * @since 1.0
  * @author zongf
  * @created 2019-07-18
  */
  public static T newInstance(Class clz, HashMap propertiesMap, DateTypeConverter typeConverter){
  // 如果属性为空, 则不进行创建, 返回null
  if (propertiesMap == null || propertiesMap.isEmpty()) {
  return null;
  }
  // 使用无参数构造方法创建对象
  T t = null;
  try {
  t = clz.newInstance();
  for (Map.Entry entry : propertiesMap.entrySet()) {
  // 获取对象属性与值
  String property = entry.getKey();
  Object value = entry.getValue();
  // 获取属性描述符
  PropertyDescriptor propertyDescriptor = new PropertyDescriptor(property, clz);
  // 获取属性类型
  Class propertyType = propertyDescriptor.getPropertyType();
  // 使用类型转换器转换参数类型
  value = typeConverter.convert(value, propertyType);
  // 调用set方法, 赋值
  Method writeMethod = propertyDescriptor.getWriteMethod();
  writeMethod.invoke(t, value);
  }
  } catch (Exception e) {
  throw new RuntimeException("反射创建对象失败!", e);
  }
  return t;
  }

  1.3 DateTypeConverter 转换器

  笔者仅仅编写了一个日期类型的转换器, 在企业开发中, 可能需要用到的转换器会更多.

/** 日期类型转换器
  * @since 1.0
  * @author zongf
  * @created 2019-07-18
  */
  public class DateTypeConverter {
  /** 转换对象的类型
  * @param value 值
  * @param javaType java类型
  * @return 转换后的类型
  * @since 1.0
  * @author zongf
  * @created 2019-07-18
  */
  public Object convert(Object value, Class javaType) {
  Object obj = value;
  // 如果是java 日期
  if(javaType.equals(Date.class)) {
  java.sql.Date date = (java.sql.Date) value;
  obj = date.toInstant().getEpochSecond();
  // 如果是java8 日期
  } else if(javaType.equals(LocalDate.class)){
  obj = ((java.sql.Date) value).toLocalDate();
  } else {
  obj = value;
  }
  return obj;
  }
  }

  2. 单元测试

  2.1 创建javaBean

  测试时, 需要创建t_user表和javabean, 笔者这边仅给出javabean的定义.

public class UserPO {
  private Integer id;
  private String name;
  private String password;
  private LocalDate birthday;
  // 省略setter/getter/toString 方法
  }

  2.2 测试用例

// 测试转换单个对象为Map 结构
  @Test
  public void toPropertyMap() throws SQLException {
  String str = "select id uId, name , pwd, birthday from t_user where id = 1001";
  Connection connection = DbConnUtil.getConnection(true);
  Statement statement = connection.createStatement();
  ResultSet resultSet = statement.executeQuery(str);
  LinkedHashMap map = ResultSetUtil.toPropertyMap(resultSet);
  map.forEach((key, val) -> System.out.println(key + ":" + val));
  }
  // 测试转换对象为单个bean
  @Test
  public void toBean() throws SQLException {
  String str = "select * from t_user where id = 1002";
  Connection connection = DbConnUtil.getConnection(true);
  Statement statement = connection.createStatement();
  ResultSet resultSet = statement.executeQuery(str);
  UserPO userPO = ResultSetUtil.toBean(resultSet, UserPO.class);
  System.out.println(userPO);
  }
  // 测试转换对象为bean列表
  @Test
  public void toBeans() throws SQLException {
  String str = "select * from t_user ";
  Connection connection = DbConnUtil.getConnection(true);
  Statement statement = connection.createStatement();
  ResultSet resultSet = statement.executeQuery(str);
  List userPOList = ResultSetUtil.toBeans(resultSet, UserPO.class);
  userPOList.forEach(System.out::println);
  }