Statement 和 PreparedStatement的比较

       PrepareStatement是继承自Statement(Statement 是 Java 执行数据库操作的一个重要接口,用于在已经建立数据库连接的基础上,向数据库发送要执行的SQL语句。Statement对象,用于执行不带参数的简单SQL语句。),有人说,如果是一个良好的程序员,他在任何时候都应该使用PrepareStatement而不是使用Statement。

      

PreparedStatement 实例包含已编译的 SQL 语句。这就是使语句“准备好”。包含于 PreparedStatement 对象中的 SQL 语句可具有一个或多个 IN 参数。IN参数的值在 SQL 语句创建时未被指定。相反的,该语句为每个 IN 参数保留一个问号(“?”)作为 占位符。每个问号的值必须在该语句执行之前,通过适当的setXXX 方法来提供。
由于 PreparedStatement 对象已 预编译过,所以其执行速度要快于 Statement 对象。因此,多次执行的 SQL 语句经常创建为 PreparedStatement 对象,以提高效率。
作为 Statement 的子类,PreparedStatement 继承了 Statement 的所有功能。另外它还添加了一整套方法,用于设置发送给数据库以取代 IN 参数 占位符的值。同时,三种方法 execute、 executeQuery 和 executeUpdate 已被更改以使之不再需要参数。这些方法的 Statement 形式(接受 SQL 语句参数的形式)不应该用于 PreparedStatement 对象
举例:
使用PrepareStatement
stringsql = "select * from people where id = ? and name = ?";
    preparedstatement ps = connection.preparestatement(sql);
    ps.setint(1,id);
    ps.setstring(2,name);
    resultset rs = ps.executequery();
     使用Statement
Connection con = DriverManager.getConnection(url, "sunny", "")
    ResultSet rs = stmt.executeQuery("SELECT a, b, c FROM Table2");
    ResultSet rs = stmt.executeQuery("SELECT a, b, c FROM Table2");
      
在JDBC应用中,如果你已经是稍有水平开发者,你就应该始终以PreparedStatement代替Statement.也就是说,在任何时候都不要使用Statement.
  基于以下的原因:
   一.代码的可读性和可维护性.
  虽然用PreparedStatement来代替Statement会使代码多出几行,但这样的代码无论从可读性还是可维护性上来说.都比直接用Statement的代码高很多档次:
  
stmt.executeUpdate("insertintotb_name(col1,col2,col2,col4)values('"+var1+"','"+var2+"',"+var3+",'"+var4+"')");
  perstmt=con.prepareStatement("insertintotb_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的语句中,即使是相同一操作,而由于每次操作的数据不同所以使整个语句相匹配的机会极小,几乎不太可能匹配.比如:
  
insertintotb_name(col1,col2)values('11','22');
  insertintotb_name(col1,col2)values('11','23');


  即使是相同操作但因为数据内容不一样,所以整个个语句本身不能匹配,没有缓存语句的意义.事实是没有数据库会对普通语句编译后的执行代码缓存.
  当然并不是所以预编译语句都一定会被缓存,数据库本身会用一种策略,比如使用频度等因素来决定什么时候不再缓存已有的预编译结果.以保存有更多的空间存储新的预编译语句.
   三.最重要的一点是极大地提高了安全性.
  即使到目前为止,仍有一些人连基本的恶义SQL语法都不知道.
  Stringsql="select*fromtb_namewherename='"+varname+"'andpasswd='"+varpasswd+"'";
  如果我们把['or'1'='1]作为varpasswd传入进来.用户名随意,看看会成为什么?
  select*fromtb_name='随意'andpasswd=''or'1'='1';
  因为'1'='1'肯定成立,所以可以任何通过验证.更有甚者:
  把[';droptabletb_name;]作为varpasswd传入进来,则:
  select*fromtb_name='随意'andpasswd='';droptabletb_name;有些数据库是不会让你成功的,但也有很多数据库就可以使这些语句得到执行.
  而如果你使用预编译语句.你传入的任何内容就不会和原来的语句发生任何匹配的关系.只要全使用预编译语句,你就用不着对传入的数据做任何过虑.而如果使用普通的statement,有可能要对drop,;等做费尽心机的判断和过虑.
在服务器方面:
通常情况下,一个预先准备好的语句(preparedstatement)是和一个单独的数据库连接相关联的.当连接关闭时,语句就被丢弃了.一般来说,一个胖客户端应用程序在得到一个数据库连接后会一直保持到程序结束.它会使用两种方法创建所有的语句:急切创建(eagerly)或者懒惰创建(lazily). Eagerly是说,当程序启动时全部创建.Lazily是说随用随创建.急切的方法会在程序启动时有些延时,但是一旦程序启动以后,运行很好.懒惰的方法启动很快,但是当程序运行时,预先准备的语句在第一次使用是创建.这就会造成性能不平衡,知道所有的语句都准备好了,但是最终程序会和急切方法一样快.哪一种最好要看你需要的是快速启动还是均衡的性能.
一个J2EE应用程序所带来的问题就是它不能像这样工作.它只在一个请求的生存时间中保持一个连接.这意味着在他处理每一个请求时都会重新创建语句,就不象胖客户端只创建一次,而不是每个请求都创建那样有效,当J2EE服务器给你的程序一个连接时,并不是一个真正的连接,而是一个经过包装的.你可以通过查看那个连接的类的名字来检验一下.它不是一个数据库的JDBC连接,是你的服务器创建的一个类.通常,如果你调用一个连接的close方法,那么jdbc驱动程序会关闭这个连接.我们希望的是当J2EE应用程序调用close的时候,连接会返回到连接池中.我们通过设计一个代理的jdbc连接类来做这些,但看起来就象是实际的连接.当我们调用这个连接的任何方法时,代理类就会把请求前递给实际的连接.
但是,当我们调用类似close的方法时,并不调用实际连接的close方法,只是简单地把连接返回给连接池,然后把代理连接标记为无效,这样当它被应用程序重新使用时,我们会得到异常.包装是非常有用的,因为它帮助J2EE应用程序服务器实现者比较聪明地加上预先准备语句的支持.当程序调用Connection.prepareStatement时,由驱动程序返回一个PreparedStatement对象.当应用程序得到它时,保存这个句柄,并且在请求完成时,关闭请求之前关闭这个句柄.但是,在连接返回到连接池之后,以后被同样或者另一个应用程序重用时,那么,我们就理论上希望同样的PreparedStatement返回给应用程序.
J2EEPreparedStatement缓冲J2EEPreparedStatement缓冲由J2EE服务器内部的连接池管理器使用一个缓冲区来实现.J2EE服务器在连接池中保存一个所有数据库的预先准备语句的一个列表.当一个程序调用一个连接的prepareStatement方法时,服务器先检查这个语句是否已经有了,如果是,相应的PreparedStatement就在缓冲区内,就返回给应用程序,如果不是,请求就会传递给jdbc驱动程序,请求/预先准备语句对象就会加入到缓冲区里.对于每一个连接我们需要一个缓冲区,因为这是jdbc驱动程序的工作要求. 任何返回的preparedStatement都是针对这个连接的.如果我们要利用缓冲区的优势,要使用和前面相同的规则.我们需要使用参数话的查询,这样它们就会和已经在缓冲区的某一个匹配.大多数应用程序服务器都允许你调整缓冲区的大小.

猜你喜欢

转载自blog.csdn.net/qq_30124241/article/details/52664247