JDBC ResultSet结果通过java反射赋值给java对象

使用反射

我们先来写一个不用框架,用JDBC来操作呢数据库,下面写一个简单的例子:


// 加载驱动程序
Class.forName(driver);
conn = DriverManager.getConnection(url, user, password);

Statement statement = conn.createStatement();
String sql = "select * from user";
ResultSet rs = statement.executeQuery(sql);

String name  = null;
String phone = null;

while (rs.next()) {
    //获取stuname这列数据
    name  = rs.getString("user_name");
    phone = rs.getString("user_phone");

    System.out.println(name + "\t" + phone);
}

就是这么简单的一段代码,然后就是想从数据库中获取相应的字段,就用get和set方法来处理,返回一个对象,写起来就是麻烦,不通用

因为java有反射的功能,所以可以抽取出来,写个通用的方法。

/**
	 * 把ResultSet的结果放到java对象中
	 * 
	 * @param <T>
	 * @param rs
	 *            ResultSet
	 * @param obj
	 *            java类的class
	 * @return
	 */
	public static <T> ArrayList<T> putResult(ResultSet rs, Class<T> obj) {
		try {
			ArrayList<T> arrayList = new ArrayList<T>();
			ResultSetMetaData metaData = rs.getMetaData();
			/**
			 * 获取总列数
			 */
			int count = metaData.getColumnCount();
			while (rs.next()) {
				/**
				 * 创建对象实例
				 */
				T newInstance = obj.newInstance();
				for (int i = 1; i <= count; i++) {
					/**
					 * 给对象的某个属性赋值
					 */
					String name = metaData.getColumnName(i).toLowerCase();
					name = toJavaField(name);// 改变列名格式成java命名格式
					String substring = name.substring(0, 1);// 首字母大写
					String replace = name.replaceFirst(substring, substring.toUpperCase());
					Class<?> type = obj.getDeclaredField(name).getType();// 获取字段类型
					Method method = obj.getMethod("set" + replace, type);
					/**
					 * 判断读取数据的类型
					 */
					if(type.isAssignableFrom(String.class)){
						method.invoke(newInstance, rs.getString(i));
					}else if(type.isAssignableFrom(int.class) || type.isAssignableFrom(Integer.class)){
						method.invoke(newInstance, rs.getInt(i));
					}else if(type.isAssignableFrom(Boolean.class) || type.isAssignableFrom(boolean.class)){
						method.invoke(newInstance, rs.getBoolean(i));
					}else if(type.isAssignableFrom(Date.class)){
						method.invoke(newInstance, rs.getDate(i));
					}
				}
				arrayList.add(newInstance);
			}
			return arrayList;
 
		} catch (InstantiationException | IllegalAccessException | SQLException | SecurityException
				| NoSuchMethodException | IllegalArgumentException | InvocationTargetException
				| NoSuchFieldException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
 
		return null;
	}
	/**
	 * 数据库命名格式转java命名格式
	 * 
	 * @param str
	 *            数据库字段名
	 * @return java字段名
	 */
	public static String toJavaField(String str) {
 
 
		String[] split = str.split("_");
		StringBuilder builder = new StringBuilder();
		builder.append(split[0]);// 拼接第一个字符
 
 
		// 如果数组不止一个单词
		if (split.length > 1) {
			for (int i = 1; i < split.length; i++) {
				// 去掉下划线,首字母变为大写
				String string = split[i];
				String substring = string.substring(0, 1);
				split[i] = string.replaceFirst(substring, substring.toUpperCase());
				builder.append(split[i]);
			}
		}
 
 
		return builder.toString();
	}

这里先简单处理这几种常用类型,其他的其实可以后续再加上。这里主要依赖反射obj.getDeclaredField(name).getType()获取字段类型,然后通过obj.getMethod(“set” + replace, type)来找到数据库java对象的这个set方法,最终method.invoke(newInstance, rs.getString(i))来附值,这么一想,如果查出来是一个很大的list的,那么该怎么优化呢?

先来使用一下,这个写的小工具,就可以不用繁琐的get和set方法:

ArrayList<userDO> userDOS = ResultSetPropertiesHelper.transferResult(rs, userDO.class);
for (UserDD userDO : userDOS) {
    System.out.println(userDO.toString());
}

上面是通过对象里面的set和get方法来设置变量的值,这样写起来感觉比较麻烦,直接用Field的set方法是不是更加简单一点,但是这样写,访问的是public对象,所以

Field field = obj.getField(name);
if (type.isAssignableFrom(String.class)) {
method.invoke(newInstance, rs.getString(i));
} else if (type.isAssignableFrom(byte.class) || type.isAssignableFrom(Byte.class)) {
method.invoke(newInstance, rs.getByte(i));// byte 数据类型是8位、有符号的,以二进制补码表示的整数
} else if (type.isAssignableFrom(short.class) || type.isAssignableFrom(Short.class)) {
method.invoke(newInstance, rs.getShort(i));// short 数据类型是 16 位、有符号的以二进制补码表示的整数
} else if (type.isAssignableFrom(int.class) || type.isAssignableFrom(Integer.class)) {
method.invoke(newInstance, rs.getInt(i));// int 数据类型是32位、有符号的以二进制补码表示的整数
} else if (type.isAssignableFrom(long.class) || type.isAssignableFrom(Long.class)) {
method.invoke(newInstance, rs.getLong(i));// long 数据类型是 64 位、有符号的以二进制补码表示的整数
} else if (type.isAssignableFrom(float.class) || type.isAssignableFrom(Float.class)) {
method.invoke(newInstance, rs.getFloat(i));// float 数据类型是单精度、32位、符合IEEE 754标准的浮点数
} else if (type.isAssignableFrom(double.class) || type.isAssignableFrom(Double.class)) {
method.invoke(newInstance, rs.getDouble(i));// double 数据类型是双精度、64 位、符合IEEE 754标准的浮点数
} else if (type.isAssignableFrom(BigDecimal.class)) {
method.invoke(newInstance, rs.getBigDecimal(i));
} else if (type.isAssignableFrom(boolean.class) || type.isAssignableFrom(Boolean.class)) {
method.invoke(newInstance, rs.getBoolean(i));// boolean数据类型表示一位的信息
} else if (type.isAssignableFrom(Date.class)) {
method.invoke(newInstance, rs.getDate(i));
}

这里补充一下ResultSetMetaData接口都有哪些信息

可以使用此对象获得列的数目和类型以及每一列的名称:

  • getColumnCount(); 返回 ResultSet 中的列数。
  • getColumnName(int); 返回列序号为 int 的列名。
  • getColumnLabel(int); 返回此列暗含的标签。
  • isCurrency(int); 如果此列包含带有货币单位的一个数字,则返回 true。
  • isReadOnly(int); 如果此列为只读,则返回 true。
  • isAutoIncrement(int); 如果此列自动递增,则返回 true。这类列通常为键,而且始终是只读的。
  • getColumnType(int); 返回此列的 SQL 数据类型。这些数据类型包括

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!