[J2EE] 有关 PreparedStatement

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

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

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

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

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

PreparedStatement(后面简称PS)
当我们说到PS的时候,可能我们会关心几个问题:

预编译发生在什么时候
PS何时被缓存起来的
回答第一个问题:

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

回答第二个问题:

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

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

代码如下:


import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;

import oracle.jdbc.OracleConnection;


public class Test {
    static Connection con = null;

    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();
        System.out.println(ps0 == ps1);//如果是true,说明cache发生了作用


        con.close();
        con = null;
        con = ((oracle.jdbc.OracleConnection) getConnection());
        PreparedStatement ps2 = getPS(con);
        ps2.close();
        con.close();
        System.out.println(ps1 == ps2);//如果是false,说明connection关闭后,会重新缓存

    }

    //获取PS实例
    public static PreparedStatement getPS(Connection con) throws ClassNotFoundException, SQLException {

        String sql = "select * from cms_turbine t where t.mapping like ?";
        PreparedStatement ps1 = con.prepareStatement(sql);
        ps1.setString(1, "hello");
        return ps1;
    }

    //建立连接
    public static Connection getConnection() {
        if (con == null) {
            try {
                Class.forName("oracle.jdbc.driver.OracleDriver");
                con = DriverManager.getConnection("jdbc:oracle:thin:@127.0.0.1:1523:XXX", xx, xx);

//下面两句是为了开启cache
                ((oracle.jdbc.OracleConnection) con).setImplicitCachingEnabled(true);
                ((oracle.jdbc.OracleConnection) con).setStatementCacheSize(10);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return con;
    }
}

执行结果是: 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

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容