利用反射和JDBC元数据编写通用的查询方法

本文介绍使用JDBC进行数据库查询的优化方法,通过PreparedStatement替代Statement,预防SQL注入,提高代码效率。文章详细讲解如何利用反射创建对象,处理查询结果,并将数据映射到Java对象的Field中。

针对上一篇文章通过JDBC对云端数据库的增删查改操作最后的一部分做的补充。

先贴代码,后给设计思路。若有对代码中的方法有疑惑的可以查看JDK帮助文档:

	/**
	 * 使用 JDBC 进行查询,将结果赋值给对象的 Field,返回存放对象的 List 
	 *调用 PreparedStatement 的 executeQuery() 方法进行查询
	 * ResultSet:结果集,封装可使用 JDBC 进行查询的结果
	 * ResultSetMetaData:JDBC元数据,可以得到结果集里列的别名等
	 * @param <T>:泛型
	 * @param sql:查询语句
	 * @param args :补充占位符的实参数组
	 * @return 
	 */
	public static <T> List<T>  getSelect(Class<T> clazz,String sql,Object...args) {
		T entity =null;
		
		//创建一个List,用于存放对象
		List<T> list = new ArrayList<>();
		
		Connection connection = null;
		PreparedStatement preparedStatement= null;
		ResultSet rs = null;
		try {
			//利用反射创建对象!!!若无此步骤,会抛出异常
			entity = clazz.newInstance();
			
			connection = getConnection();
			//1.获取 preparedStatement 对象
			preparedStatement = connection.prepareStatement(sql);
			//2.向 sql 补充占位符变量
			for(int i = 0;i<args.length;i++) {
				preparedStatement.setObject(i+1, args[i]);
			}
			//3.获得查询结果:ResultSet 对象
			rs = preparedStatement.executeQuery();
			
			//4.获取 ResultSetMetaData 对象
			ResultSetMetaData rsmd = rs.getMetaData();
			
			//5.创建一个Map<String,Object>存放查询结果,键:列的别名(别名应与对象的属性值同名),值:列对应的值
			Map<String,Object> map = new HashMap<>();
			//6.处理结果集
			while(rs.next()) {
				//6.1利用 ResultSetMetaData 填充Map
				for(int i= 0;i<rsmd.getColumnCount() ;i++) {
					String columnName = rsmd.getColumnLabel(i+1);
					Object columnValue = rs.getObject(i+1);
					map.put(columnName, columnValue);
				}
				//6.2 判断map 是否为空,并为对象的字段赋值
				if(map.size()>0) {
					for(Map.Entry<String, Object> entry : map.entrySet()) {
						String fieldName = entry.getKey();
						Object fieldValue = entry.getValue();
						//System.out.println(fieldName+ ":"+fieldValue);
						//根据 fieldName 得到entity 实例对应的字段值并将 fieldValue 赋值给它
						setFiledValue(entity, fieldName, fieldValue);
					}					
				}
				//7. 返回对象放入list 中
				list.add(entity);
				//清空 map,下次循环继续存键值
				map.clear();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}finally {
			release(rs, preparedStatement, connection);
		}
		return list;
	}
	//向对象的对应属性赋值
	private static void setFiledValue(Object  obj,String filedName,Object value) {
		Class clazz = obj.getClass();
		try {	
			Field field = null;
			field = clazz.getDeclaredField(filedName);
			field.setAccessible(true);
			field.set(obj, value);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

思路解析:

先说一下比较上一篇文章优化的地方:使用 PreparedStatement(Statement的子接口) 代替了 Statement,优点: 使用PreparedStatement 使代码更简洁;防止SQL注入攻击

实现思路:

  1. 获取与远程数据库的连接,得到查询结果集,
  2. 利用 ResultSetMetad 的 getColumnCount() 方法获取列的数量,getColumnLabel() 方法获取列的别名,将结果集存入 Map<String,Object> 中.
  3. 遍历 Map ,根据其 key 得到对象的 Field 名并将其 value 值赋给 Field。
  4. 遍历完 Map 后,对象的所有 Field 都被赋值了,然后将对象存入 List<T> 中,至此一行结果处理完毕
  5. 重复 2 3 4 步骤,直到结果集读完,即 ResultSet.next() 为 false。
  6. 返回存放对象的 List

 接下来测试一下这个方法: 

     @Test
    public void testGetSelect() {
        Class<Person> clazz = Person.class;
        String sql = "SELECT id AS Id,name AS Name,address AS Address from first_table WHERE address =?";
        Object [] args = {"成都"};
        List<Person> persons = new ArrayList<>();
        persons = JDBCTools.getSelect(clazz, sql, args);
        System.out.println(persons.toString());
    }

结果:

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值