利用 Java 反射机制简化 JDBC ResultSet 封装实体类

描述

查询完某个表之后,一般都是把结果的每一个字段注入到一个实体类中。比如,数据库 users 表,查询出来的结果注入到 User 实体类中。

通过 while 遍历 ResultSet,通过对应 getXxx() 获取数据封装到实体类中。每一个实体类的字段都不一样,又重新写重复的注入实体类的操作代码,非常麻烦,通过反射可以简化这样的操作。

案例

public List<User> selectAll() {
  List<User> users = new ArrayList<>();
  try {
    Connection connection = DriverManager.getConnection(config.getUrl(), config.getUsername(), config.getPassword());
    PreparedStatement statement = connection.prepareStatement("select * from users");
    ResultSet rs = statement.executeQuery();
    while (rs.next()) {
      User user = new User();
      user.setId(rs.getInt("id"));
      user.setAge(rs.getInt("age"));
      user.setAvatar(rs.getString("avatar"));
      user.setShow_name(rs.getString("show_name"));
      users.add(user);
    }
  } catch (SQLException e) {
    throw new RuntimeException(e);
  }
  return users;
}

students 表的查询结果,注入数据到 Student 实体类中。users 表,又得写差不多的重复代码,也只是 new 实体类以及循环体内的注入代码发生了变化。

通过反射优化

jnject 专门来处理如何把 ResultSet 结果注入到实体类中。需要传递一个实体类的反射对象,类型是泛型:

private List<T> inject(ResultSet rs, Class<T> clz) {
  List<T> list = new ArrayList<>();
  try {
    while (rs.next()) {
      T t = clz.getDeclaredConstructor().newInstance();
      for (Field field : clz.getDeclaredFields()) {
        field.setAccessible(true);
        if (field.getType().getName().equals(String.class.getName())) {
          field.set(t, rs.getString(field.getName()));
        } else if (field.getType().getName().equals(int.class.getName())) {
          field.set(t, rs.getInt(field.getName()));
        } else if (field.getType().getName().equals(java.util.Date.class.getName())) {
          field.set(t, rs.getDate(field.getName()));
        }
      }
      list.add(t);
    }
  } catch (SQLException | InvocationTargetException | InstantiationException | IllegalAccessException |
           NoSuchMethodException e) {
    throw new RuntimeException(e);
  }
  return list;
}

通过反射创建实体类对象,再获取这个实体类对象的所有字段,不管你是 private、public、protected 修饰的字段都可以获取,所以,必须通过 getDeclaredFields() 函数来获取对象的字段。

在 for 循环体中,我做了一个判断,判断实体类字段的类型是什么类型,针对类型去从结果集中获取相应类型的值,再通过 Field 对象的 set 函数给实体类的属性注入值。

public List<T> selectAll(Class<T> clz) {
  List<T> list;
  try {
    Connection connection = DriverManager.getConnection(config.getUrl(), config.getUsername(), config.getPassword());
    PreparedStatement statement = connection.prepareStatement("select * from users");
    list = inject(statement.executeQuery(), clz); // 调用 inject 函数,完成实体类注入
  } catch (SQLException e) {
    throw new RuntimeException(e);
  }
  return list;
}

测试效果

public static void main(String[] args) {
  MySQLConfig config = new LoadConfig<>(MySQLConfig.class).getConfig();

  List<User> users = new Simple<User>(config).selectAll(User.class);
  System.out.println(Arrays.toString(users.toArray()));
}

无需再关注如何把结果集注入到实体类中,而只需要提供一个实体类的反射对象即可完成查询操作。

仓库

title:(Java 学习 - 反射机制) cover:(https://www.infocode.com.cn/blog/wp-content/uploads/2021/10/f8fba7a2f3c35d3d7c16892b38ba4785.jpg) link:(https://gitee.com/Himmelbleu/java-learning/tree/main/basic/reflect)
posted @ 2022-10-18 16:22  Himmelbleu  阅读(47)  评论(0编辑  收藏  举报