在JDBC应用中,有经验的开发者,大多都使用PreparedStatement代替Statement
那么,这是为什么呢?
原因基于以下几点:
1.代码的可读性和可维护性
首先,让我们来看看使用Statement的查询SQL语句,只能采用拼接的方法传入参数
int id = 1;
String name = "rachel";
String sql = "select * from memo_group where id='"+id+"'or name='"+name+"'";
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery(sql);
而PreparedStatement可以传入带占位符的SQL语句,提供了补充占位符变量的方法
String sql = "select * from memo_group where id = ? or name= ? ";
prepareStatement preStatement = connection.prepareStatement(sql);
preStatement.setInt(1,groupId);
preStatement.setString(2,groupName);
ResultSet resultSet = statement.executeQuery();
由上述例子可知,PreparedStatement可以实现SQL语句的参数化
PreparedStatement接口继承Statement,作为 Statement 的子类,PreparedStatement 继承了Statement 的所有功能。另外它还添加了一整套方法,用于设置发送给数据库以取代 IN 参数占位符的值。
包含于 PreparedStatement 对象中的 SQL 语句可具有一个或多个 IN 参数。IN参数的值在 SQL 语句创建时未被指定。该语句为每个 IN 参数保留一个问号(“?”)作为占位符。每个问号的值必须在该语句执行之前,通过适当的setXXX 方法来提供。
in(?)输入一个参数
String sql = "select id,name,created_time,modify_time from memo_group where name in(?) ";
statement = connection.prepareStatement(sql);
//参数赋值 parameterIndex 1开始
statement.setString(1,names);
resultSet = statement.executeQuery();
public static void main(String[] args) {
//where name in (?)
//where name in ('C++','default');
//? 不能多作为多参数,多个参数就写多个?
queryMemoGroup("'C++','default'");
}
注意:?不能作为多参数,需要传入多个参数时,就需要写多个?
in(?)输入多个参数
StringBuilder sb = new StringBuilder();
sb.append("select id,name,created_time,modify_time from memo_group where name in(");
for (int i = 0; i < names.length; i++) {
sb.append("?,");
}
sb.setLength(sb.length()-1);
sb.append(")");
statement = connection.prepareStatement(sb.toString());
//参数赋值 parameterIndex 1开始
for (int i=0;i<names.length;i++){
statement.setString(i+1, names[i]);
}
public static void main(String[] args) {
//where name in (?)
//where name in ('C++','default');
queryMemoGroup("C++","default","Java");
}
2.PreparedStatement尽最大可能提高性能
(1)Statement
Statement statement = conn.createStatement(); //创建
ResultSet rSet = statement.executeQuery(sql); //执行
(2)PreparedStatement
PreparedStatement preStatement = conn.prepareStatement(sql); //创建
ResultSet pSet = preStatement.executeQuery(); //执行
由上可以看出,PreparedStatement有预编译的过程,已经绑定SQL,之后无论执行多少遍(修改SQL语句中的参数),都不会再去进行编译,而 statement 不同,执行多少次,就要编译多少次SQL,因此,当执行数据量特别大时,PreparedStatement的数据库访问效率要远远高于 Statement。
3.防止SQL注入
首先,什么是SQL注入呢?让我们先来看一个例子:
String sql = "select * from memo_group where id='"+id+"' or '1=1'";
因为"1=1"肯定成立,所以该SQL语句判断条件也一定成立
在使用参数化查询的情况下,数据库系统不会将参数的内容视为SQL指令的一部分来处理,而是在数据库完成SQL指令的编译后,才套用参数运行,因此就算参数中含有破坏性的指令,也不会被数据库所运行。而Statement采用字符拼接的方式,一旦有人恶意使用破坏性指令,就会造成数据库泄漏。
使用PreparedStatement的参数化的查询可以阻止大部分的SQL注入