PreparedStatement的预编译

一、我们先来看一下sql的执行过程:
在Oracle里执行一个SQL语句,一般都要经过下面几个步骤:
 Create a Cursor 创建游标;
 Parse the Statement 解析语句;
 Bind Any Variables 绑定变量;
 Run the Statement 运行语句;
 Close the Cursor 关闭游标;
如果是一个查询SQL,则还要经过下面的步骤:
 Describe Results of a Query 描述查询的结果集;
 Define Output of a Query 定义查询的输出数据;
 Fetch Rows of a Query 获取查询出来的行。

二、SQL解析过程
从上面的步骤可以看出,每执行一个SQL,都需要对它进行解析(Parse),而一个解析过程,需要完成下面的工作:
 语法检查,验证它是否是合法的语句,有没有语法错误;
 语义检查,实现数据字典的查找,以验证是否符合表和列的定义,类型是否正确;
 (如果是CBO优化模式,关于CBO,请看后面Oracle的优化器一章)收集参考对象的统计;
 在所要求的对象上获取语法分析锁,使得在语句的语法分析过程中不改变这些对象的定义;
 检查用户的权限是否足够;
 从许多可能的执行路径中选择此语句最佳的执行计划;
 将它装入共享SQL区;
 生成语句的编译版本(P-CODE)。
解析是一个昂贵的操作,因为解析过程中需要消耗许多资源,而且费时,正因为如此,Oracle创造了共享池的概念,共享池会自动将解析过的SQL缓存起来,以后碰到相同的SQL,就不用再解析了,这样可以大大提高SQL的执行速度。

三、 缓存SQL的原理
ORACLE执行SQL语句时,先将SQL语句的字串通过一个hash算法得出一个hash值,然后检查共享池中是否已存在这个哈希值,若有就用已缓存的执行计划来执行这个语句(即缓存命中,后面我们会提到共享池的命中率,就是这个概念),若没有(即缓存缺失)则需进行解析。
由于Oracle是通过SQL字符的hash值来判断是否为相同的SQL语句,因此,如果你的SQL有一点小小的变换,在Oracle看来,就是另外一个SQL了,会对它进行重新解析。
例如:

  1. select id, name from members where id = 1403   
  2. select id, name From members where id = 1403   
  3. select name, id from members where id = 1403  

这三条SQL在Oracle看来就是三条不同的SQL。

 绑定变量

在大部分时候,sql语句里有一些经常会变化的值。例如:

  1. select id, name from members where id = 1207   
  2. select id, name from members where id = 1208   
  3. select id, name from members where id = 1209  

前面说过了,这样的SQL其实是三条不同的SQL,因为它们的字符明显不一样。那我们该怎么样才能让它们成为同一条SQL呢?可以通过绑定变量来实现。
下面是一条含绑定变量的sql 语句:

  1. select id, name from members where id = :member_id  

这样不管member_id如何变化,Oracle都会认为这条SQL是同一条,就可以节省解析的成本了。
那么,在java开发中,怎么使用绑定变量呢?注意,不要认为下面的代码是在使用绑定变量:

  1. Statement stmt=conn.createStatement();   
  2. String member_id=member.id;   
  3. String sql="select id,name from members where id ="+member_id;   
  4. stmt.executeQuery(sql);  

上面的例子里,当member.id的值为1207时,我们传给stmt的SQL实际上是:

  1. select id, name from members where id = 1207  

当member.id的值为1208时,就是:

  1. select id, name from members where id = 1208  

它们在Oracle看来仍然是不同的SQL。
其实,在java中使用绑定变量非常简单,只需要使用PreparedStatement对象就可以了。如下:

  1. String sql="select id,name from members where id =?";   
  2. PreparedStatement pstmt=conn.createStatement(sql);   
  3. pstmt.setString(1,member.id);//将member.id传给第一个问号。  

这样,PreparedStatement会自动把这条SQL在传给Oracle时转化为类似下面的SQL:

  1. select id, name from members where id = :member_id  

这样就实现了绑定变量,它只需解析一次,不管member.id如何变化,都不用再做解析了。

猜你喜欢

转载自ikon.iteye.com/blog/1132255