【Java学习笔记】——Statement & PrepareStatement

来源:互联网 发布:淘宝店铺被投诉售假 编辑:程序博客网 时间:2024/05/22 11:30


       刚一开始,不了解的时候,去网上看,上边将PrepareStatement的逼格吹得很高,比如说:在JDBC应用中,如果你已经是稍有水平的开发者,你就应该始终以PrepareStatement代替Statement。也就是说,在任何时候都不要使用Statement。看到这里,小编内心不禁暗暗的下定决心,以后也要使用PrepareStatement。


       那么,在真实的开发中,它们两者又是孰优孰劣呢?

      上一篇,在JDBC创建对象并执行SQL语句的时候,我们可以知道,两者在这方面的区别:


[java] view plain copy
 print?
  1. <span style="font-family:Microsoft YaHei;font-size:14px;">        //创建语句执行对象      
  2.         //需要执行的sql语句    
  3.         String sql = "select user_name,password from t_user where user_id=?";    
  4.         //使用Statement对象    
  5.        Statement st=conn.createStatement();          
  6.        //使用PrepareStatement对象    
  7.        PreparedStatement pst = conn.createStatement(sql);    
  8.            
  9.         //执行语句      
  10.         //使用Statement对象    
  11.         st.executeQuery("sql");      
  12.         //使用PrepareStatement对象    
  13.         pst.executeQuery(); </span>  

可以看到,两者在使用时,使用sql的位置不同。PrepareStatement采用了预编译的方式,而Statement采用的是执行时创建。


      在JDBC的API中,Statement要求开发者付出大量的时间和精力,在使用Statement获取JDBC访问时,所具有的一个共同的问题就是输入适当的格式的日期和时间戳。而这个问题,通过采用PrepareStatement可以自动解决。同时,PrepareStatement的另外一个优点字符串不是动态创建的。


      OK,说到这里,我们可以初步断定,网上把PrepareStatement的逼格吹得那么高,还是有一定的缘由的。那么,为什么要说:在JDBC应用中,如果你已经是稍有水平的开发者,你就应该始终以PrepareStatement代替Statement。难道使用PrepareStatement仅仅就是为了逼格高一些吗?显然不是吧,下边小编就给您好好的唠唠。

      JDBC驱动的最佳化基于使用的是什么功能。选择PrepareStatement还是Statement取决于你要怎样使用它们。对于执行一次的SQL语句,选择Statement是最好的;相反,如果SQL语句需要被多次执行,那么我们可以选用PrepareStatement。


1、PrepareStatement 提高性能


      PrepareStatement属于预编译的方式,每一种数据库都会尽最大努力对预编译语句提供最大的性能优化。因为预编译语句有可能被重复调用,所以sql语句在被数据库的编译器编译后,执行代码被缓存下来,那么下次调用的时候,只要相同的预编译语句就不需要编译,只要将参数直接传入编译过的语句执行代码中,就会执行。当然,这并不是说只有一个Connection中多次执行的预编译语句被缓存,而是对整个数据库中。只要预编译的语句和缓存中相匹配,那么在任何时候就可以  再次进行编译就可以直接执行。

      而在Statement的语句中,即使是相同的操作,由于每次操作的数据不同,所以整个语句相匹配的机会极小,几乎不太可能完全匹配。比如:


[java] view plain copy
 print?
  1. <span style="font-family:Microsoft YaHei;font-size:14px;">    insert into tb_name (col1,col2) values ('11','22');  
  2.     insert into tb_name (col1,col2) values ('11','23');</span>  

       即使是相同的操作,但是因为插入的数据内容不同,所以整个语句本身并不能匹配,没有缓存语句的意义。

       当然,并不是所有的预编译语句都一定会被缓存,数据库本身会使用一定的策略。比如使用访问频率等指标,来决定什么时候不在缓存已有的预编译结果,以保存更多的存储空间来存储新的预编译语句。


2、提高代码的可读性和可维护性


一个数据库插入的Demo

[java] view plain copy
 print?
  1. <span style="font-family:Microsoft YaHei;font-size:14px;">    //执行一个插入操作  
  2.       
  3.     //Statement  
  4.     Statement st;  
  5.     st.executeUpdate("insert into tb_name (col1,col2,col2,col4) values ('"+var1+"','"+var2+"',"+var3+",'"+var4+"')");  
  6.   
  7.     //PrepareStatement  
  8.     PreparedStatement pst;  
  9.     pst = con.prepareStatement("insert into tb_name (col1,col2,col2,col4) values (?,?,?,?)");  
  10.     pst.setString(1,var1);  
  11.     pst.setString(2,var2);  
  12.     pst.setString(3,var3);  
  13.     pst.setString(4,var4);  
  14.     pst.executeUpdate();</span>  

      虽然,使用PrepareStatement会比Statement多几行代码,但是这样的改变,无论是从代码可读性还是可维护性上来讲,都是值得的。


3、安全性


PrepareStatement防止了SQL注入,提高了程序的安全性。 

[java] view plain copy
 print?
  1. <span style="font-family:Microsoft YaHei;font-size:14px;">String sql = "select * from tb_name where name= '"+varname+"' and passwd='"+varpasswd+"'";</span>  

在这里,如果我们把[‘or’1'='1]作为varpasswd传入进来,用户名随意填写,那么就会产生SQL注入问题。


[java] view plain copy
 print?
  1. <span style="font-family:Microsoft YaHei;font-size:14px;">select * from tb_name = '随意' and passwd = '' or '1' = '1';</span>  

因为‘1’=‘1’肯定成立,所以可以通过任何验证者。

更进一步,把[';drop table tb_name;]作为varpasswd传入进来

[java] view plain copy
 print?
  1. <span style="font-family:Microsoft YaHei;font-size:14px;">select * from tb_name = '随意' and passwd = '';drop table tb_name;</span>  

在某些数据库上,这些语句完全可以执行。

       当然,如果使用预编译语句,传入的任何内容就不会和原来的语句发生任何匹配的关系。使用PrepareStatement,预编译语句的话,就不用对传入的数据做任何考虑;如果使用Statement,就需要对drop等做判断,防止SQL注入。

       刚一开始,不了解的时候,去网上看,上边将PrepareStatement的逼格吹得很高,比如说:在JDBC应用中,如果你已经是稍有水平的开发者,你就应该始终以PrepareStatement代替Statement。也就是说,在任何时候都不要使用Statement。看到这里,小编内心不禁暗暗的下定决心,以后也要使用PrepareStatement。


       那么,在真实的开发中,它们两者又是孰优孰劣呢?

      上一篇,在JDBC创建对象并执行SQL语句的时候,我们可以知道,两者在这方面的区别:


[java] view plain copy
 print?
  1. <span style="font-family:Microsoft YaHei;font-size:14px;">        //创建语句执行对象      
  2.         //需要执行的sql语句    
  3.         String sql = "select user_name,password from t_user where user_id=?";    
  4.         //使用Statement对象    
  5.        Statement st=conn.createStatement();          
  6.        //使用PrepareStatement对象    
  7.        PreparedStatement pst = conn.createStatement(sql);    
  8.            
  9.         //执行语句      
  10.         //使用Statement对象    
  11.         st.executeQuery("sql");      
  12.         //使用PrepareStatement对象    
  13.         pst.executeQuery(); </span>  

可以看到,两者在使用时,使用sql的位置不同。PrepareStatement采用了预编译的方式,而Statement采用的是执行时创建。


      在JDBC的API中,Statement要求开发者付出大量的时间和精力,在使用Statement获取JDBC访问时,所具有的一个共同的问题就是输入适当的格式的日期和时间戳。而这个问题,通过采用PrepareStatement可以自动解决。同时,PrepareStatement的另外一个优点字符串不是动态创建的。


      OK,说到这里,我们可以初步断定,网上把PrepareStatement的逼格吹得那么高,还是有一定的缘由的。那么,为什么要说:在JDBC应用中,如果你已经是稍有水平的开发者,你就应该始终以PrepareStatement代替Statement。难道使用PrepareStatement仅仅就是为了逼格高一些吗?显然不是吧,下边小编就给您好好的唠唠。

      JDBC驱动的最佳化基于使用的是什么功能。选择PrepareStatement还是Statement取决于你要怎样使用它们。对于执行一次的SQL语句,选择Statement是最好的;相反,如果SQL语句需要被多次执行,那么我们可以选用PrepareStatement。


1、PrepareStatement 提高性能


      PrepareStatement属于预编译的方式,每一种数据库都会尽最大努力对预编译语句提供最大的性能优化。因为预编译语句有可能被重复调用,所以sql语句在被数据库的编译器编译后,执行代码被缓存下来,那么下次调用的时候,只要相同的预编译语句就不需要编译,只要将参数直接传入编译过的语句执行代码中,就会执行。当然,这并不是说只有一个Connection中多次执行的预编译语句被缓存,而是对整个数据库中。只要预编译的语句和缓存中相匹配,那么在任何时候就可以  再次进行编译就可以直接执行。

      而在Statement的语句中,即使是相同的操作,由于每次操作的数据不同,所以整个语句相匹配的机会极小,几乎不太可能完全匹配。比如:


[java] view plain copy
 print?
  1. <span style="font-family:Microsoft YaHei;font-size:14px;">    insert into tb_name (col1,col2) values ('11','22');  
  2.     insert into tb_name (col1,col2) values ('11','23');</span>  

       即使是相同的操作,但是因为插入的数据内容不同,所以整个语句本身并不能匹配,没有缓存语句的意义。

       当然,并不是所有的预编译语句都一定会被缓存,数据库本身会使用一定的策略。比如使用访问频率等指标,来决定什么时候不在缓存已有的预编译结果,以保存更多的存储空间来存储新的预编译语句。


2、提高代码的可读性和可维护性


一个数据库插入的Demo

[java] view plain copy
 print?
  1. <span style="font-family:Microsoft YaHei;font-size:14px;">    //执行一个插入操作  
  2.       
  3.     //Statement  
  4.     Statement st;  
  5.     st.executeUpdate("insert into tb_name (col1,col2,col2,col4) values ('"+var1+"','"+var2+"',"+var3+",'"+var4+"')");  
  6.   
  7.     //PrepareStatement  
  8.     PreparedStatement pst;  
  9.     pst = con.prepareStatement("insert into tb_name (col1,col2,col2,col4) values (?,?,?,?)");  
  10.     pst.setString(1,var1);  
  11.     pst.setString(2,var2);  
  12.     pst.setString(3,var3);  
  13.     pst.setString(4,var4);  
  14.     pst.executeUpdate();</span>  

      虽然,使用PrepareStatement会比Statement多几行代码,但是这样的改变,无论是从代码可读性还是可维护性上来讲,都是值得的。


3、安全性


PrepareStatement防止了SQL注入,提高了程序的安全性。 

[java] view plain copy
 print?
  1. <span style="font-family:Microsoft YaHei;font-size:14px;">String sql = "select * from tb_name where name= '"+varname+"' and passwd='"+varpasswd+"'";</span>  

在这里,如果我们把[‘or’1'='1]作为varpasswd传入进来,用户名随意填写,那么就会产生SQL注入问题。


[java] view plain copy
 print?
  1. <span style="font-family:Microsoft YaHei;font-size:14px;">select * from tb_name = '随意' and passwd = '' or '1' = '1';</span>  

因为‘1’=‘1’肯定成立,所以可以通过任何验证者。

更进一步,把[';drop table tb_name;]作为varpasswd传入进来

[java] view plain copy
 print?
  1. <span style="font-family:Microsoft YaHei;font-size:14px;">select * from tb_name = '随意' and passwd = '';drop table tb_name;</span>  

在某些数据库上,这些语句完全可以执行。

       当然,如果使用预编译语句,传入的任何内容就不会和原来的语句发生任何匹配的关系。使用PrepareStatement,预编译语句的话,就不用对传入的数据做任何考虑;如果使用Statement,就需要对drop等做判断,防止SQL注入。

0 0
原创粉丝点击