JDBC程序的一个常用优化技术是使用PrepareStatement类。如果代码多次发出同类查询,则使用预定义语句来加快执行数据库操作。比如查找用户密码,则需要重复发出表单查询:SELECT passwd FROM Credentials WHERE username=...
预定义语句要求数据库预编译查询——也就是说,解析SQL语句并且计算查询策略。信息是使用预定义语句保持的,并且在重新发出查询时重复使用。
使用Connection类的prepareStation方法创建预定义语句。为每个参数使用“?”字符:
PreparedStatement stat=conn.prepareStatement("SELECT password FROM Credentials WHERE username=?");
当准备发出预定义语句时,首先设置参数值:
stat.setString(1,name);
注意索引值1表示第一个参数。然后以通常的方式发出语句:
ResultSet result = stat.executeQuery();
乍一看,好像预定义语句在Web应用程序中没有什么好处。毕竟,当完成一个用户请求时,就关闭连接。预定义语句与数据库连接相关,当数据库的物理连接终止时,建立它的所有工作都将丢失。
但如果物理数据库连接保持在一个连接中,那么当检索连接时,预定义语句仍然很可能可供用。许多连接池实现将缓冲预定义语句。
当调用prepareStatement时,连接池将首先查看语句缓冲内部,使用查询字符串作为键。如果发现了预定义语句,那么重用它。否则创建新的预定义语句并且添加到缓存中。
所以这些活动对于应用程序编程人员而言都是透明的。你请求PrepareStatement对象,并且希望至少在一段时间内,连接池能够为给定的查询检索现有对象。
警告:不能在一个请求作用域之外保持和重用PreparedStatement对象。一旦关闭连接池中的连接,所有相关的PrepareStatement对象也归还到连接池中。因此,在当前请求之外,不应该保持PrepareStatement对象。替代的方法是,使用同一查询字符串来调用prepareStatement方法,并有可能会得到一个缓冲的语句对象。
注意:即使你并不关注性能,也有另一个使用预定义语句的正当理由:防止SQL注入攻击。当查询有连接的SQL代码和用户输入形成时,恶意用户可以在输入中提供能修改查询含义的SQL代码。使用预定义语句,用户输入将永远不会被解释为SQL。
》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》
PS:什么是SQL注入及实例说明:
例如:String sql="select * from usert where name='"+usernameInput+"' and password='"+pwdinput+"'";
这句是判断当uesrname和password条件都成立时,才会显示结果。
就是用如下的字符串拼接而成
String sql="select * from usert where username='"
+usernameInput
+"' and password='"
+pwdinput
+"'";
在输入用户名hh,密码输入下面的代码,
pwd' or '1'='1
在数据库报务器相当于执行这样的SQL语句:
select * from usert where name='hh' and password='pwd' or '1'='1' ; --这个语句不管前面的条件是否成立,后面的语句总是为真,因此where 条件语句总是成立。
所以就可以跳过前面用户名和密码的判段。
这样就不安全
//正常的SQL查询语句
String sql="select * from usert where name='"+userinput+"' and password='"+pwdinput+"'";
//userinput用户输入的字符串
String userinput="hh";
//pwdinput接收用户输入的字符串
String pwdinput="pwd' or '1'='1";
这种不安全的情况是在SQL语句在拼接的情况下发生
为了主防范这样”SQL注入安全“可以用预编译解决:
String sql= "insert into userlogin values(?,?)";
try {
PreparedStatement ps=conn.prepareStatement(sql);
for(int i=1;i<100;i++){
ps.setInt(1, i);
ps.setInt(2, 8888);
ps.executeUpdate();
}
ps.close();
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
这样,不管用户输入是什么,数据库服务器总认为是一个值,而SQL语句是相同的不会重复编译,也不会编译新的SQL语句,所以不管用户怎么输入,都不会产生"SQL注入安全"