如何使用PreparedStatement

Statement执行过程

一个sql语句执行过程中,将经历这么几个步骤:

  1. 传输sql到数据库。
  2. 数据库检查sql的语法合法性,并解析sql。
  3. 计算Access Plan。数据库会通过检测index,statistics来给出最优的访问计划。
  4. 根据访问计划进行检索,返回数据。

在上面步骤中,第3步是非常耗时的。因此,为了提高性能,数据库会缓存执行语句以及其Access Plan。这被称为statement cache。在statement cache中,sql语句本身为key,access plan为value。当相同的sql语句被发送过来时,数据库会使用缓存中的access plan以节省cpu时间。

看下面一段code:

Statement statement = connection.createStatement();
String sql1="Select * from test where id=1";
String sql2="Select * from test where id=";
statement.execute(sql1);
statement.execute(sql1);
statement.execute(sql1);
statement.execute(sql2+"2");
statement.execute(sql2+"3");

sql1在第一次执行的时候,需要计算执行计划。但在第2和3次执行的时候,会使用缓存好的执行计划,因此后面的sql1不会再重新检验语法与计算执行计划,效率会比第一次高。

sql2却每次都在变化,在cache中,key为整个sql语句,所以每次sql2都无法命中cache,即使它仅仅参数不同,也必须重新检验语法与计算执行计划,效率自然就低下。

强大的数据库会在cache命中上做优化,但复杂的语句还是避免不了miss。

PreparedStatement的存在是为了避免sql2的劣势。看下面code。

String sql2="Select * from test where id=?";
PreparedStatement pstmt = connection.prepareStatement(sql2);
pstmt.setInt(1,2);
pstmt.executQuery();
pstmt.setInt(1,3);
pstmt.executQuery();

PreparedStatement在创建的时候,会将参数化的语句发送给数据库,进行语法检测和执行计划计算。Cache中的key将是参数化的语句。当后面preparedstatement在执行的时候,每次均会命中cache,使用已存在的access plan进行检索。

包含以上优点,PreparedStatement的优点归为:

  1. 预编译,节省后面使用的时间。
  2. 可防止sql注入。

Statement的生命周期

在了解statement执行过程以后,还需要了解其生命周期。Cache的生命周期。这样我们在设计自己的数据库访问层时,才不会犯自以为正确的错误。

Connection conn = DriverManager.getConnection(...);
Statement stmt = conn.createStatement();
stmt.execute(sql);
stmt.close();
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.execute();
pstmt.close();
conn.close();

上面的方法展示了statement的生命周期. 无论是statement还是preparedStatement, 它们都绑定在一个connection上. 调用statement的close方法或connection的close方法,均会导致statement的关闭.

在数据库引擎一端,statement缓存会跟创建它的connection绑定在一起(如果我错了,请纠正我).一旦关闭connection, cache就失效了. 通过下面试验检验这个结论.

  • 在同一个connection下面, 多次执行同一条语句,发现第一次执行时间要长,后面的几次时间相近,但都比第一次要短.
  • 在不同的connection下面,执行同一条语句,发现每次执行时间均很长,且时间相近.

从上述试验,推断出,cache绑定与其connection.

所以,如果只是执行一次语句,那么preparedstatement并不会带来性能优势.只有在保持同一链接的情况下,频繁执行相似的语句,preparedstatement才会带来性能提高.这需要我们在设计ORM框架时要对此注意.

J2EE server下的statement的生命周期

在J2EE应用中,每一个http request均是一个thread处理. 程序员会在DAO层这样写他的代码:

Connection conn = datasource.getConnection();
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.execute();
pstmt.close();
conn.close();

思考一下,在这个thread中,创建了connection, connection又创建了preparedstatement, 最后connection关闭. 倘若每一个http request都这样做, preparedstatement根本没有任何作用. 每次预编译和产生的cache均随着connection的关闭而失效了.

若使preparedstatement能够发挥其优势,则必须让它的生命周期跨越所有的http request线程. J2EE server使用两个功能帮助preparedStatement扩张其生命周期.

  1. Thread Pool
  2. Statement Cache

每一个J2EE服务器均提供thread pool. 它包装了Connection, 使Connection的创建并不是真的创建,只是从pool中拿出一个存在的链接; 而Connection的关闭也只是将它放回pool中,而不是真正关闭.

Statement Cache并不是所有服务器均有的功能, 如tomcat,若想使用statement cache,则需要扩展其API. 此Statement Cache是存在于server JVM堆中的cache, 它缓存了被创建出的preparedStatement. 当调用preparedStatement的关闭方法, 也只是将其放回cache.

时间: 2024-12-29 13:35:50

如何使用PreparedStatement的相关文章

JDBC系列:(3)使用PreparedStatement执行sql语句

执行sql语句的接口 接口 作用 Statement接口 用于执行静态的sql语句 PreparedStatement接口 用于执行预编译sql语句 CallableStatement接口 用于执行存储过程的sql语句(call xxx) PreparedStatement Vs Statement 序号 不同 描述 1 语法不同 PreparedStatement可以使用预编译的sql,而Statment只能使用静态的sql 2 效率不同 PreparedStatement可以使用sql缓存区

JDBC编程之预编译SQL与防注入式攻击以及PreparedStatement的使用教程

在JDBC编程中,常用Statement.PreparedStatement 和 CallableStatement三种方式来执行查询语句,其中 Statement 用于通用查询, PreparedStatement 用于执行参数化查询,而 CallableStatement则是用于存储过程. 1.Statement       该对象用于执行静态的 SQL 语句,并且返回执行结果. 此处的SQL语句必须是完整的,有明确的数据指示.查的是哪条记录?改的是哪条记录?都要指示清楚.     通过调用

使用PreparedStatement接口操作数据库

从代码来看,用PreparedStatement来代替Statement会使代码多出几行,但这样的代码无论从可读性还是可维护性上来说,都比直接用Statement的代码高很多档次. 传递给PreparedStatement对象的参数可以被强制进行类型转换,使开发人员可以确保在插入或查询数据时与底层的数据库格式匹配. 在公共web站点环境下,有恶意的用户会利用那些设计不完善的.不能正确处理字符串的应用程序来个SQl注入,使用PreparedStatement安全性更高. package cn.bd

jdbc中的Statement对象和Preparedstatement对象的区别,以及通过jdbc操作调用存储过程

一. java.sql.*   和  javax.sql.*的包的类结构 |- Driver接口: 表示java驱动程序接口.所有的具体的数据库厂商要来实现此接口. |- connect(url, properties):  连接数据库的方法. url: 连接数据库的URL URL语法: jdbc协议:数据库子协议://主机:端口/数据库 user: 数据库的用户名 password: 数据库用户密码 |- DriverManager类: 驱动管理器类,用于管理所有注册的驱动程序 |-regis

PreparedStatement接口

从实际来讲,Statement现在已经不使用了,他已经称为了历史. Statement执行关键性问题在于他需要一个完整 的字符串定义要使用的SQL语句,而PreparedStatement可以动态的设置所需要的数据. PrepareStatement属于Statement的子接口但是想要取得这个子接口的实例化对象,依然要使用COnnection提供的方法. prepareStatement PreparedStatement prepareStatement(String sql) throws

Statement和PreparedStatement的区别; 什么是SQL注入,怎么防止SQL注入? (转)

问题一:Statement和PreparedStatement的区别 先来说说,什么是java中的Statement:Statement是java执行数据库操作的一个重要方法,用于在已经建立数据库连接的基础上,向数据库发送要执行的SQL语句.具体步骤: 1.首先导入java.sql.*:这个包. 2.然后加载驱动,创建连接,得到Connection接口的的实现对象,比如对象名叫做conn. 3.然后再用conn对象去创建Statement的实例,方法是:Statement stmt = conn

关于PreparedStatement的setDate的了解

今天写代码的时候需要往数据库里插入Date类型的时间,用到PreparedStatement的时候发现setDate()方法老是给我提示出错,结果一查,发现PreparedStatement的setDate()方法的第二个参数用的时间类型是java.sql.Date,所以当我用java.util.Date来设置时就错了. 那对于java.sql.Date和java.util.Date之间应该如何转换呢?经过查资料可以这样转换: java.util.Date utilDate = new java

PreparedStatement的用法

转载:http://www.cnblogs.com/raymond19840709/archive/2008/05/12/1192948.html jdbc(java database connectivity,java数据库连接)的api中的主要的四个类之一的java.sql.statement要求开发者付出大量的时间和精力.在使用statement获取jdbc访问时所具有的一个共通的问题是输入适当格式的日期和时间戳:2002-02-05 20:56 或者 02/05/02 8:56 pm. 

PreparedStatement

一.or ture or安全问题-------------利用PreparedStatement接口解决        解决办法:使用Statement的子接口PreparedStatement来解决.        即:            PreperedStatement的参数使用占位符替代,            能通过setXxxx()方法为占位符赋值,            在赋值的过程中动态检测,预防SQL注入问题的发生.        code:            Stri

PreparedStatement批量处理和事务

[java] view plain copy PreparedStatement批量处理和事务代码如下: /* * PreparedStatement: 1.addBatch() 将一组参数添加到 PreparedStatement对象内部 2.executeBatch() 将一批参数提交给数据库来执行,如果全部命令执行成功,则返回更新计数组成的数组. * */ public class PreparedStatementCommitAndRollback { public static voi