HikariCP 参数化查询

时间 2018/6/19 22:15:24 加载中...

在【HikariCP简单使用】中,我们都是使用的自己拼写的SQL语句,而通过字符串连接产生的SQL语句,都可能会导致 SQL注入 的发生。

SQL注入:

在字符串拼接的时候,用户输入的部分,用户恶意的输入了字符串 导致非预期的结果。

比如: select * from user where username = 'tom' 

tom 应该是用户输入的字符串,假如用户恶意输入 tom ' or '1'='1

则拼接后的字符串为 select * from user where username = ' tom' or '1'='1' 

则会将所有结果查询出来,如果恶意输入 delete 语句,后果则不堪设想。

参数化查询


参数化查询是解决SQL注入漏洞的好办法。


参数化的新增

/* 新增-参数化 */
public static int add_demo_with_param() {
	int result = -1;
	try {
		HikariDataSource dataSource = getDataSource();
		Connection connection = dataSource.getConnection();

		String sql = "insert user values(NULL,?,?,?)";
		PreparedStatement statement = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
		statement.setObject(1, "10");
		statement.setObject(2, "10");
		statement.setObject(3, 0);
		statement.execute();

		ResultSet resultSet = statement.getGeneratedKeys();
		if (resultSet != null) {
			if (resultSet.next())
				result = resultSet.getInt(1);
		}
		
		if (connection != null && !connection.isClosed())
			connection.close();
		if (dataSource != null && !dataSource.isClosed())
			dataSource.close();
		
	} catch (Exception e) {
		e.printStackTrace();
	}
	return result;
}


在参数化查询中,不再是 Connection 直接 CreateStatement,而是先 prepareStatement 先准备一个 Statement,然后再为参数赋值。


参数化更新 

/*参数化更新*/
public static int update_demo_with_param() {
	try {

		HikariDataSource dataSource = getDataSource();
		Connection connection = dataSource.getConnection();

		String sql = "update user set username = ? where userid = ?";
		PreparedStatement statement = connection.prepareStatement(sql);
		statement.setObject(1, "changed");
		statement.setObject(2, 5);
		return statement.executeUpdate();

	} catch (Exception e) {
		e.printStackTrace();
	}
	return -1;
}


参数化查询

public static ResultSet queryDemo() {
	try {
		/* HikariDataSource 是需要关闭的 */
		HikariDataSource dataSource = getDataSource();
		Connection connection = dataSource.getConnection();

		String sql = "select * from user where usercode = ?";
		PreparedStatement statement = connection.prepareStatement(sql);
		statement.setObject(1, "admin");
		ResultSet resultSet = statement.executeQuery();
	

		if (connection != null && !connection.isClosed())
			connection.close();
		if (dataSource != null && !dataSource.isClosed())
			dataSource.close();
		
		return resultSet;

	} catch (Exception e) {
		e.printStackTrace();
	}
	
	return null;
}


ResultSet 和 List<T> 的转化


上面的参数化查询,其实不应该返回 ResultSet,连接关闭后,ResultSet也不能访问了,将返回的 ResultSet 集合转成对应的 List<T> 才是不错的。

那如何转换呢,我们主要用到了 反射 的方法。

public static <T> List<T> toList(ResultSet resultSet, Class<T> type) throws SQLException, InstantiationException,
			IllegalAccessException, NoSuchFieldException, SecurityException {
		List<T> list = new ArrayList<T>();

		if (resultSet != null) {

			ResultSetMetaData md = resultSet.getMetaData();// 获取键名
			int columnCount = md.getColumnCount();// 获取行的数量

			while (resultSet.next()) {

				// 此类要有默认的构造函数
				T instance = type.newInstance();
				Field[] fields = type.getDeclaredFields();
				
				for (int i = 1; i <= columnCount; i++) {
					String colName = md.getColumnName(i);
					Object val = resultSet.getObject(i);
								
					for (Field field : fields) {
						if (field.getName().equalsIgnoreCase(colName)) {				
							field.setAccessible(true);
							field.set(instance, val);
						}
					}
				}
				
				list.add(instance);
				
			}
		}

		return list;
	}


ResultSet 和 List<HashMap<String, String>> 的转化


public List<HashMap<String, String>> toListHashMap(ResultSet rs) throws SQLException {
	if (rs == null)
		return null;
	
	List<HashMap<String, String>> result = new ArrayList<HashMap<String, String>>();
	HashMap<String, String> map = new HashMap<String, String>();

	ResultSetMetaData md = rs.getMetaData(); // 得到结果集的结构信息,比如字段数、字段名等
	int columnCount = md.getColumnCount(); // 返回此 ResultSet 对象中的列数

	while (rs.next()) {
		map = new HashMap<String, String>(columnCount);
		for (int i = 1; i <= columnCount; i++) {
			map.put(md.getColumnName(i), String.valueOf(rs.getObject(i)));
		}
		
		result.add(map);
	}

	return result;
}

总体来说,参数化的方法比较简单,不管是新增,还是更新或查询,都是先准备一个 Statement,然后再为其变量赋值。


版权说明
作者:SQBER
文章来源:http://blog.sqber.com/articles/HikariCP-query-with-param.html
本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。