PreparedStatement 预编译原理

preparedStatement 有三大优点:

一.代码的可读性和可维护性.

二.PreparedStatement尽最大可能提高性能.

三.最重要的一点是极大地提高了安全性.

 

其中一和三的确很易理解。 关于性能的提高也是最有价值的这点, 我对其原理还有些质疑。 

 

 

网上一:
SQL 语句被预编译并且存储在 PreparedStatement 对象中,其后可以使用该对象高效地多次执行该语句。

问题:预编译在JDBC中完成的?还是数据库中?即便存在preparedStatement中,如果这个preparedStatement 对象不被缓存起来,其后又如何重复使用,多次执行? (一些webserver会缓存preparedStatement)

我想关于其性能的提高主要得需要数据库的支持。

oracle 性能优化 写道
共享SQL语句
  为了不重复解析相同的SQL语句,在第一次解析之后, ORACLE将SQL语句存放在内存中。
可惜的是ORACLE只对简单的表提供高速缓冲(cache buffering) ,这个功能并不适用于多表连接查询。

个人理解:预编译是利用数据库的SQL共享来实现的,因为当使用preparedStatement 的时候,尽管参数不同,但是在语句中用?来替代。 因此很多语句就完全相同。(这个道理和J2EE的server缓存preparedStatement 的道理应该是一样的)

 

问题:但是如果是这样的话,那么oracle的多表查询岂不是并无预编译效果?

 

问题:关于缓存:是否根据创建preparedStatement的语句来做为Key来map的?例如以下:是否检查匹配insert into tb_name (col1,col2,col2,col4) values (?,?,?,?)来确定是否用已经有的执行路径来执行?

perstmt = con.prepareStatement("insert into tb_name (col1,col2,col2,col4) values (?,?,?,?)");

 

以下几篇从网络上搜索到的三篇文章:

 

优快云 写道
一.代码的可读性和可维护性.
虽然用PreparedStatement来代替Statement会使代码多出几行,但这样的代码无论从可读性还是可维护性上来说.都比直接用Statement的代码高很多档次:

stmt.executeUpdate("insert into tb_name (col1,col2,col2,col4) values ('"+var1+"','"+var2+"',"+var3+",'"+var4+"')");

perstmt = con.prepareStatement("insert into tb_name (col1,col2,col2,col4) values (?,?,?,?)");
perstmt.setString(1,var1);
perstmt.setString(2,var2);
perstmt.setString(3,var3);
perstmt.setString(4,var4);
perstmt.executeUpdate();

不用我多说,对于第一种方法.别说其他人去读你的代码,就是你自己过一段时间再去读,都会觉得伤心.

二.PreparedStatement尽最大可能提高性能.
每一种数据库都会尽最大努力对预编译语句提供最大的性能优化.因为预编译语句有可能被重复调用.所以语句在被DB的编译器编译后的执行代码被缓存下来,那么下次调用时只要是相同的预编译语句就不需要编译,只要将参数直接传入编译过的语句执行代码中(相当于一个涵数)就会得到执行.这并不是说只有一个Connection中多次执行的预编译语句被缓存,而是对于整个DB中,只要预编译的语句语法和缓存中匹配.那么在任何时候就可以不需要再次编译而可以直接执行.而statement的语句中,即使是相同一操作,而由于每次操作的数据不同所以使整个语句相匹配的机会极小,几乎不太可能匹配.比如:
insert into tb_name (col1,col2) values ('11','22');
insert into tb_name (col1,col2) values ('11','23');
即使是相同操作但因为数据内容不一样,所以整个个语句本身不能匹配,没有缓存语句的意义.事实是没有数据库会对普通语句编译后的执行代码缓存.这样每执行一次都要对传入的语句编译一次.

当然并不是所以预编译语句都一定会被缓存,数据库本身会用一种策略,比如使用频度等因素来决定什么时候不再缓存已有的预编译结果.以保存有更多的空间存储新的预编译语句.

三.最重要的一点是极大地提高了安全性.

即使到目前为止,仍有一些人连基本的恶义SQL语法都不知道.
String sql = "select * from tb_name where name= '"+varname+"' and passwd='"+varpasswd+"'";
如果我们把[' or '1' = '1]作为varpasswd传入进来.用户名随意,看看会成为什么?

select * from tb_name = '随意' and passwd = '' or '1' = '1';
因为'1'='1'肯定成立,所以可以任何通过验证.更有甚者:
把[';drop table tb_name;]作为varpasswd传入进来,则:
select * from tb_name = '随意' and passwd = '';drop table tb_name;有些数据库是不会让你成功的,但也有很多数据库就可以使这些语句得到执行.

而如果你使用预编译语句.你传入的任何内容就不会和原来的语句发生任何匹配的关系.(前提是数据库本身支持预编译,但上前可能没有什么服务端数据库不支持编译了,只有少数的桌面数据库,就是直接文件访问的那些)只要全使用预编译语句,你就用不着对传入的数据做任何过虑.而如果使用普通的statement,有可能要对drop,;等做费尽心机的判断和过虑.

 

 

论坛 写道
1,执行效率:Statement 采取直接编译 SQL 语句的方式,扔给数据库去执行,而 PreparedStatement 则先将 SQL 语句预编译一遍,再填充参数,这样效率会高一些。JDK 文档说:SQL 语句被预编译并且存储在 PreparedStatement 对象中,其后可以使用该对象高效地多次执行该语句。

2,代码可读性:Statement 中 SQL 语句中需要 Java 中的变量,加就得进行字符串的运算,还需要考虑一些引号、单引号的问题,参数变量越多,代码就越难看,而且会被单引号、双引号搞疯掉;而 PreparedStatement,则不需要这样,参数可以采用“?”占位符代替,接下来再进行参数的填充,这样利于代码的可读性,并且符合面向对象的思想。

3,安全性:Statement 由于可能需要采取字符串与变量的拼接,很容易进行 SQL 注入攻击,而 PreparedStatement 由于是预
编译,再填充参数的,不存在 SQL 注入问题。

 

Oracle 优化 写道
3. 共享SQL语句
  为了不重复解析相同的SQL语句,在第一次解析之后, ORACLE将SQL语句存放在内存中。这块位于系统全局区域SGA(system global area)的共享池(shared buffer pool)中的内存可以被所有的数据库用户共享。 因此,当你执行一个SQL语句(有时被称为一个游标)时,如果它和之前的执行过的语句完全相同, ORACLE就能很快获得已经被解析的语句以及最好的执行路径。 ORACLE的这个功能大大地提高了SQL的执行性能并节省了内存的使用。

  可惜的是ORACLE只对简单的表提供高速缓冲(cache buffering) ,这个功能并不适用于多表连接查询。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值