PreparedStatement

l
它是
Statement
接口的子接口;

l 强大之处:


  • SQL
    攻击;
  • 提高代码的可读性、可维护性;
  • 提高效率!

l
学习
PreparedStatement
的用法:

  • 如何得到
    PreparedStatement
    对象:

¨
给出
SQL
模板!

¨
调用
Connection

PreparedStatement prepareStatement(String sql
模板
)

¨
调用
pstmt

setXxx()
系列方法
sql
模板中的
?
赋值!

¨
调用
pstmt

executeUpdate()

executeQuery()
,但它的方法都没有参数。

l 预处理的原理

  • 服务器的工作:

¨
校验
sql
语句的语法!

¨ 编译:一个与函数相似的东西!

¨ 执行:调用函数

  • PreparedStatement

¨ 前提:连接的数据库必须支持预处理!几乎没有不支持的!

¨
每个
pstmt
都与一个
sql
模板绑定在一起,先把
sql
模板给数据库,数据库先进行校验,再进行编译。执行时只是把参数传递过去而已!

¨ 若二次执行时,就不用再次校验语法,也不用再次编译!直接执行!

1
什么是
SQL
攻击

在需要用户输入的地方,用户输入的是
SQL
语句的片段
,最终用户输入的

SQL
片段与我们
DAO
中写的
SQL
语句合成一个完整的
SQL
语句!例如用户在登录时输入的用户名和密码都是为
SQL
语句的片段!

2
演示
SQL
攻击

首先我们需要创建一张用户表,用来存储用户的信息。

CREATE TABLE user(

uid
CHAR(32) PRIMARY KEY,

username
VARCHAR(30) UNIQUE KEY NOT NULL,

PASSWORD
VARCHAR(30)

);

INSERT INTO user VALUES('U_1001', 'zs', 'zs');

SELECT * FROM user;

现在用户表中只有一行记录,就是
zs

下面我们写一个
login()
方法!

public
void
login(String username, String password) {

Connection con =
null
;

Statement stmt =
null
;

ResultSet rs =
null
;

try
{

con = JdbcUtils.
getConnection
();

stmt = con.createStatement();

String sql = "SELECT * FROM user WHERE " +

"username='" + username +

"' and password='" + password + "'";

rs = stmt.executeQuery(sql);

if
(rs.next()) {

System.
out
.println("
欢迎
"
+ rs.getString("username"));

}
else
{

System.
out
.println("
用户名或密码错误!
"
);

}

}
catch
(Exception e) {

throw
new
RuntimeException(e);

}
finally
{

JdbcUtils.
close
(con, stmt, rs);

}

}

下面是调用这个方法的代码:

login("a' or 'a'='a", "a' or 'a'='a");

这行当前会使我们登录成功!因为是输入的用户名和密码是
SQL
语句片段,最终与我们的
login()
方法中的
SQL
语句组合在一起!我们来看看组合在一起的
SQL
语句:

SELECT * FROM tab_user WHERE username='
a' or 'a'='a
' and password='
a' or 'a'='a
'

3
防止
SQL
攻击

l 过滤用户输入的数据中是否包含非法字符;

l 分步交验!先使用用户名来查询用户,如果查找到了,再比较密码;

l
使用
PreparedStatement

4

PreparedStatement
是什么?

PreparedStatement
叫预编译声明!

PreparedStatement

Statement
的子接口,你可以使用
PreparedStatement
来替换
Statement

PreparedStatement
的好处:

l
防止
SQL
攻击;

l 提高代码的可读性,以可维护性;

l 提高效率。

5

PreparedStatement
的使用

l
使用
Connection

prepareStatement(String sql)
:即创建它时就让它与一条
SQL
模板绑定;

l
调用
PreparedStatement

setXXX()
系列方法为问号设置值

l
调用
executeUpdate()

executeQuery()
方法,但要注意,调用没有参数的方法;

String sql = “select * from tab_student where s_number=?”;

PreparedStatement pstmt = con.prepareStatement(sql);

pstmt.setString(1, “S_1001”);

ResultSet rs = pstmt.executeQuery();

rs.close();

pstmt.clearParameters();

pstmt.setString(1, “S_1002”);

rs = pstmt.executeQuery();

在使用
Connection
创建
PreparedStatement
对象时需要给出一个
SQL
模板,所谓
SQL
模板就是有“
?
”的
SQL
语句,其中“
?
”就是参数。

在得到
PreparedStatement
对象后,调用它的
setXXX()
方法为“
?
”赋值,这样就可以得到把模板变成一条完整的
SQL
语句,然后再调用
PreparedStatement
对象的
executeQuery()
方法获取
ResultSet
对象。

注意
PreparedStatement
对象独有的
executeQuery()
方法是没有参数的,而
Statement

executeQuery()
是需要参数(
SQL
语句)的。因为在创建
PreparedStatement
对象时已经让它与一条
SQL
模板绑定在一起了,所以在调用它的
executeQuery()

executeUpdate()
方法时就不再需要参数了。

PreparedStatement
最大的好处就是在于重复使用同一模板,给予其不同的参数来重复的使用它。这才是真正提高效率的原因。

所以,建议大家在今后的开发中,无论什么情况,都去需要
PreparedStatement
,而不是使用
Statement

useServerPrepStmts
参数

默认使用
PreparedStatement
是不能执行预编译的,这需要在
url
中给出
useServerPrepStmts=true参数(MySQL Server 4.1之前的版本是不支持预编译的,而Connector/J在5.0.5以后的版本,默认是没有开启预编译功能的)。

例如:jdbc:mysql://localhost:3306/test?
useServerPrepStmts=true

这样才能保证
mysql
驱动会先把
SQL
语句发送给服务器进行预编译,然后在执行
executeQuery()
时只是把参数发送给服务器。

cachePrepStmts
参数

当使用不同的
PreparedStatement
对象来执行相同的
SQL
语句时,还是会出现编译两次的现象,这是因为驱动没有缓存编译后的函数
key
,导致二次编译。如果希望缓存编译后函数的
key
,那么就要设置
cachePrepStmts
参数为
true
。例如:

jdbc:mysql://localhost:3306/test?useServerPrepStmts=true&cachePrepStmts=true

标签: none

添加新评论