[J2EE] 有关 PreparedStatement

  今天同事遇到一个问题,简言之,就是PreparedStatement的预编译究竟是怎么发挥作用的...

  嘿嘿,说来惭愧,我以前就只知道PreparedStatement比Statement要好,要防SQL注入,就要用PS。

  But,它的预编译作用,我却没关注过。现在经过一番研究,我不敢说我已经“了解”了,但是总归是思路清晰了一些,

我把我理解的整理出来希望对大家也有所帮助,有不对的地方请大家指正。

  •   预编译

  首先要说的就是“预编译”。我们知道数据库在执行SQL语句的时候,首先要解析这个SQL语句,有一个“执行计划”的概念(差不多就是指寻找执行这个sql语句的一个最优路径)。对于经常执行的sql,数据库会把执行计划给缓存起来,对应的key就是该条sql。那么当再次执行同样的sql的时候,数据库就不再解析了,直接根据key返回相应的执行计划。这就节省了很多时间。

  • PreparedStatement(后面简称PS)

  当我们说到PS的时候,可能我们会关心几个问题:

  1. 预编译发生在什么时候
  2. PS何时被缓存起来的

  回答第一个问题:

     “在创建PreparedStatement 对象时就指定了SQL语句,该语句立即发送给DBMS进行编译”。

  回答第二个问题:

     是当我们执行PS的close()方法时~~~~

  •  TEST

我针对PS cache,做了一个测验,想法是针对同一个SQL,如果我每次取到的PS都是同一个,说明确实是拿的缓存的....

代码如下:

 1 import java.sql.Connection;
 2 import java.sql.DriverManager;
 3 import java.sql.PreparedStatement;
 4 import java.sql.SQLException;
 5 
 6 import oracle.jdbc.OracleConnection;
 7 
 8 
 9 public class Test
10 {
11      static Connection con = null;
12     public static void main(String args[]) throws ClassNotFoundException, SQLException 
13     {
14         con = ((oracle.jdbc.OracleConnection)getConnection());
15         PreparedStatement ps0 = getPS(con);
16         ps0.close();
17         PreparedStatement ps1 = getPS(con);
18         ps1.close();
19         System.out.println(ps0==ps1);//如果是true,说明cache发生了作用
20         
21         
22         con.close();
23         con = null;
24         con = ((oracle.jdbc.OracleConnection)getConnection());
25         PreparedStatement ps2 = getPS(con);
26         ps2.close();
27         con.close();
28         System.out.println(ps1==ps2);//如果是false,说明connection关闭后,会重新缓存
29     
30     }
31     
32     //获取PS实例
33     public static PreparedStatement getPS(Connection con)throws ClassNotFoundException, SQLException{
34           
35            String sql = "select * from cms_turbine t where t.mapping like ?";
36            PreparedStatement ps1 = con.prepareStatement(sql);
37            ps1.setString(1, "hello");
38           return ps1;
39     }
40     
41     //建立连接
42     public static Connection getConnection(){
43         if(con == null){
44         try{
45             Class.forName("oracle.jdbc.driver.OracleDriver");
46             con = DriverManager.getConnection("jdbc:oracle:thin:@127.0.0.1:1523:XXX",xx,xx);
47 
48 //下面两句是为了开启cache
49             ((oracle.jdbc.OracleConnection)con).setImplicitCachingEnabled(true);
50             ((oracle.jdbc.OracleConnection)con).setStatementCacheSize(10);
51        }catch(Exception e){
52            e.printStackTrace();
53        }
54         }
55         return con;
56     }
57 }

执行结果是: true

                  false

说明同一个connection中两次取到的PS是同一个实例,不同connection不共用PS。

Note:

如果想要reuse PS,那再用完一个之后一定要close,意味着释放资源。在上面的代码中,如果main方法改写如下:

  public static void main(String args[]) throws ClassNotFoundException, SQLException 
    {
        con = ((oracle.jdbc.OracleConnection)getConnection());
        PreparedStatement ps0 = getPS(con);
        ps0.close();
        PreparedStatement ps1 = getPS(con);
//        ps1.close();  ps1不close,后面ps2拿到的还是ps0吗?
        System.out.println(ps0==ps1);
        
        
        PreparedStatement ps2 = getPS(con);
        ps2.close();
        con.close();
        System.out.println(ps0==ps2);
    }

执行结果是:true (因为ps0 close了)

                 false(因为ps1没有close,所以ps2并没有能拿到缓存中的PS)

参考:

http://www.linuxidc.com/Linux/2011-07/38776.htm

http://docs.oracle.com/cd/E11882_01/java.112/e10589/stmtcach.htm#JJDBC28649

原文地址:https://www.cnblogs.com/simple-code/p/4608181.html