PreparedStatement和Statement的区别和效率

同样也是在一次面试中问到的,当时回答说PreparedStatement比Statement效率高;

其实这个回答是错误的!掌握的还是不够!


一、PreparedStatement相比于Statement,有三个优点:
一)代码的可读性和可维护性。
从代码来看,用PreparedStatement来代替Statement会使代码多出几行,但这样的代码无论从可读性还是可维护性上来说,都比直接用Statement的代码高很多档次。

二)PreparedStatement尽最大可能提高性能。
每一种数据库都会尽最大努力对预编译语句提供最大的性能优化,因为预编译语句有可能被重复调用,所以语句在被DB的编译器编译后的执行代码被缓存下来,那么下次调用时只要是相同的预编译语句就不需要编译,只要将参数直接传入编译过的语句执行代码中(相当于一个函数)就会得到执行。这并不是说只有一个Connection中多次执行的预编译语句被缓存,而是对于整个DB中,只要预编译的语句语法和缓存中匹配,那么在任何时候就可以不需要再次编译而可以直接执行。而statement的语句中,即使是相同一操作,而由于每次操作的数据不同所以使整个语句相匹配的机会极小,几乎不太可能匹配。比如:
insert into tb_name (col1,col2) values ('11','22');
insert into tb_name (col1,col2) values ('11','23');
即使是相同操作但因为数据内容不一样,所以整个个语句本身不能匹配,没有缓存语句的意义,事实是没有数据库会对普通语句编译后的执行代码缓存。
当然并不是所有预编译语句都一定会被缓存,数据库本身会用一种策略,比如使用频度等因素来决定什么时候不再缓存已有的预编译结果,以保存有更多的空间存储新的预编译语句。
用Statement对象时,每次执行一个SQL命令,都会对它进行解析编译,而PreparedStatement对象在多次执行同一个SQl语句时都只解析编译一次。PreparedStatement对象“就像一条生产线,批量生产同一型号的产品速度非常快。”这样便可极大地减少资源开销。

三)极大地提高了安全性。
传递给PreparedStatement对象的参数可以被强制进行类型转换,使开发人员可以确保在插入或查询数据时与底层的数据库格式匹配。
在公共Web站点环境下,有恶意的用户会利用那些设计不完善的、不能正确处理字符串的应用程序来个SQl注入,那就有麻烦。

二、例子:
两者的代码:
1)Statement代码
Statement stmt = con.createStatement();
String sql = "INSERT INTO dept VALUES (12, 'GAME', 'BeiJing')";
stmt.executeUpdate(sql);

2)PreparedStatement代码
String sql = "INSERT INTO dept VALUES ( ?, ?, ? )";
PreparedStatement pre = con.prepareStatement(sql);
pre.setInt(1, deptno); //部门编号
pre.setString(2,dname); //部门名称
pre.setString(3,location); //部门地址
pre.executeUpdate();


下面是转载别人的博客中的内容,觉得说的挺通俗易懂:

其实PreparedStatement和Statement这俩干的活儿都一样,就是创建了一个对象然后去通过对象调用executeQuery方法来执行sql语句。说是CreateStatement和PrepareStatement的区别,但其实说的就是Statement和PrepareStatement的区别,相信大家在网上已经看到过不少这方面的资料和博客,我在此处提几点,大家看到过的,就当重记忆,没看到就当补充~下面开始谈谈他们的区别。

最明显的区别,就是执行的sql语句格式不同。我们往上放两段代码来看看他们的区别把:

代码背景:我们有一个数据库,里面有一个user表,有username,userpwd两列。我们要查出这两列的数据。

这是使用CreateStatement方法创建了stmt对象,再通过他查询的一部分语句片段。

[java]  view plain  copy
  1. String sql = "select * from users where  username= '"+username+"' and userpwd='"+userpwd+"'";  
  2. stmt = conn.createStatement();  
  3. rs = stmt.executeQuery(sql);  
而下面则是使用了PrepareStatement方法创建了pstmt对象,再通过这个对象查询的一部分语句片段。
[java]  view plain  copy
  1. String sql = "select * from users where  username=? and userpwd=?";  
  2. pstmt = conn.prepareStatement(sql);  
  3. pstmt.setString(1, username);  
  4. pstmt.setString(2, userpwd);  
  5. rs = pstmt.executeQuery();  

相信写到这,大家很多人就能看出来了,原来PrepareStatement跟Statement的主要区别就是把上面sql语句中的变量抽出来了。这就是我要说的第一大优点,PrepareStatement可以提高代码的可读性。什么?你没觉得这有什么可以提高可读性的?那好,咱来看看下面这两段代码,看完你再说话。

代码背景:我们有一个数据库,里面有一个book表,有bookid,bookname,bookauthor,booksort,bookprice五列。我们要向这个表中添加一部分数据。

Statement版

[java]  view plain  copy
  1. String sql = "insert into book (bookid,bookname,bookauthor,booksort,bookprice) values ('"+var1+"',  
  2.                                 '"+var2+"',"+var3+",'"+var4+","+var5+"')";  
  3. stmt = conn.createStatement();  
  4. rs = stmt.executeUpdate(sql);  

ParperStatement版

[java]  view plain  copy
  1. String sql = "insert into book (bookid,bookname,bookauthor,booksort,bookprice) values (?,?,?,?,?)";  
  2. pstmt = conn.prepareStatement(sql);  
  3. pstmt.setString(1,var1);  
  4. pstmt.setString(2,var2);  
  5. pstmt.setString(3,var3);  
  6. pstmt.setString(4,var4);  
  7. pstmt.setString(5,var5);  
  8. pstmt.executeUpdate();  
怎么样。反正我打这行代码的时候,整个引号逗号就给我刺激懵了。

下面说说第二点优点。ParperStatement提高了代码的灵活性和执行效率。

PrepareStatement接口是Statement接口的子接口,他继承了Statement接口的所有功能。它主要是拿来解决我们使用Statement对象多次执行同一个SQL语句的效率问题的。ParperStatement接口的机制是在数据库支持预编译的情况下预先将SQL语句编译,当多次执行这条SQL语句时,可以直接执行编译好的SQL语句,这样就大大提高了程序的灵活性和执行效率。

最后但也是最重要的一个大大的比Statement好的优点,那就是安全!

你说啥?这还关安全啥事儿,那我给你一行代码,你来给我说说这是干嘛的。

[java]  view plain  copy
  1. String sql = "select * from user where username= '"+varname+"' and userpwd='"+varpasswd+"'";  
  2. stmt = conn.createStatement();  
  3. rs = stmt.executeUpdate(sql);  

 
这是验证用户名密码的,对吧。但要是我们把'or '1' = 1'当作密码传进去,你猜猜会发生啥。 
 
[java]  view plain  copy
  1. select * from user where username = 'user' and userpwd = '' or '1' = '1';  
 
发现了吧!这是个永真式,因为1永远等于1。所以不管怎样都能获取到权限。哇。这就坏咯!这还不是最坏的,你再看! 

[java]  view plain  copy
  1. String sql = "select * from user where username= '"+varname+"' and userpwd='"+varpasswd+"'";  
  2. stmt = conn.createStatement();  
  3. rs = stmt.executeUpdate(sql);  

 

依旧是这行代码。这次我们把'or '1' = 1';drop table book;当成密码传进去。哇!又坏了!这次直接把表给删了。但是,你如果用PrepareStatement的话就不会出现这种问题。你传入的这些数据根本不会跟原来的数据有任何的交集,也不会发生这些问题。


 小总结:用Prepared statement进行开发。Prepared statement是预编译的,而statement不是,在每次执行sql语句的增删改时,如果是一条数据两者没差距,但如果数据量大于1,那么每次执行sql语句statement都要重新编译一次,而Prepared statement不用,Prepared statement的运行效率大于statement;从代码的可维护性和可读性来说,虽然用Prepared statement来代替statement会使代码多出几行,但这样的代码无论从可读性还是可维护性来说,都比直接使用statement的代码高很多档次;最重要的一点,从安全角度来说,使用Prepared statement可以大大提高程序的安全性,因为Prepared statement是用‘?’传参,可以防止sql注入,具有安全性,而statement用的是‘+’字符串拼接,安全性较低。      

  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值