转载自:http://www.importnew.com/5006.html

PreparedStatement是用来运行SQL查询语句的API之中的一个,Java提供了 Statement、PreparedStatement 和 CallableStatement三种方式来运行查询语句,当中 Statement 用于通用查询。 PreparedStatement 用于运行參数化查询,而 CallableStatement则是用于存储过程。

同一时候PreparedStatement还常常会在Java面试被提及,譬如:Statement与PreparedStatement的差别以及怎样避免SQL注入式攻击?这篇教程中我们会讨论为什么要用PreparedStatement?使用PreparedStatement有什么样的优势?PreparedStatement又是怎样避免SQL注入攻击的?

PreparedStatement是什么?
PreparedStatement是java.sql包以下的一个接口。用来运行SQL语句查询,通过调用connection.preparedStatement(sql)方法能够获得PreparedStatment对象。数据库系统会对sql语句进行预编译处理(假设JDBC驱动支持的话)。预处理语句将被预先编译好,这条预编译的sql查询语句能在将来的查询中重用,这样一来。它比Statement对象生成的查询速度更快。以下是一个样例:

  1. public class PreparedStmtExample {
  2. public static void main(String args[]) throws SQLException {
  3. Connection conn = DriverManager.getConnection("mysql:\\localhost:1520", "root", "root");
  4. PreparedStatement preStatement = conn.prepareStatement("select distinct loan_type from loan where bank=?");
  5. preStatement.setString(1, "Citibank");
  6. ResultSet result = preStatement.executeQuery();
  7. while(result.next()){
  8. System.out.println("Loan Type: " + result.getString("loan_type"));
  9. }
  10. }
  11. }
  12. Output:
  13. Loan Type: Personal Loan
  14. Loan Type: Auto Loan
  15. Loan Type: Home Loan
  16. Loan Type: Gold Loan

这个样例中,假设还是用 PreparedStatement 做相同的查询,哪怕參数值不一样,比方:”Standard Chated” 或者”HSBC”作为參数值,数据库系统还是会去调用之前编译器编译好的运行语句(系统库系统初次会对查询语句做最大的性能优化)。默认会返回”TYPE_FORWARD_ONLY”类型的结果集( ResultSet ),当然你也能够使用preparedstatment()的重载方法返回不同类型的结果集。

预处理语句的优势

PreparedStatement提供了诸多优点,企业级应用开发中强烈推荐使用PreparedStatement来做SQL查询。以下列出PreparedStatement几点优势:

PreparedStatement能够写动态參数化的查询

用PreparedStatement你能够写带參数的sql查询语句,通过使用相同的sql语句和不同的參数值来做查询比创建一个不同的查询语句要好,以下是一个參数化查询:

SELECT interest_rate FROM loan WHERE loan_type=?
如今你能够使用不论什么一种loan类型如:”personal loan”,”home loan” 或者”gold loan”来查询。这个样例叫做參数化查询,由于它能够用不同的參数调用它,这里的”?

”就是參数的占位符。

PreparedStatement比 Statement 更快

使用 PreparedStatement 最重要的一点优点是它拥有更佳的性能优势,SQL语句会预编译在数据库系统中。

运行计划相同会被缓存起来,它同意数据库做參数化查询。使用预处理语句比普通的查询更快,由于它做的工作更少(数据库对SQL语句的分析,编译,优化已经在第一次查询前完毕了)。为了降低数据库的负载,生产环境中德JDBC代码你应该总是使用PreparedStatement 。值得注意的一点是:为了获得性能上的优势。应该使用參数化sql查询而不是字符串追加的方式。以下两个SELECT 查询,第一个SELECT查询就没有不论什么性能优势。

SQL Query 1:字符串追加形式的PreparedStatement

String loanType = getLoanType();
PreparedStatement prestmt = conn.prepareStatement("select banks from loan where loan_type=" + loanType);

SQL Query 2:使用參数化查询的PreparedStatement

PreparedStatement prestmt = conn.prepareStatement("select banks from loan where loan_type=?

");
prestmt.setString(1,loanType);

第二个查询就是正确使用PreparedStatement的查询,它比SQL1能获得更好的性能。

PreparedStatement能够防止SQL注入式攻击
假设你是做Java web应用开发的,那么必须熟悉那声名狼藉的SQL注入式攻击。去年Sony就遭受了SQL注入攻击,被盗用了一些Sony play station(PS机)用户的数据。在SQL注入攻击里,恶意用户通过SQL元数据绑定输入,比方:某个站点的登录验证SQL查询代码为:

strSQL = "SELECT * FROM users WHERE name = '" + userName + "' and pw = '"+ passWord +"';"

恶意填入:

userName = "1' OR '1'='1";
passWord = "1' OR '1'='1";
那么终于SQL语句变成了:

strSQL = "SELECT * FROM users WHERE name = '1' OR '1'='1' and pw = '1' OR '1'='1';"
由于WHERE条件恒为真,这就相当于运行:

strSQL = "SELECT * FROM users;"
因此能够达到无账号password亦可登录站点。

假设恶意用户要是更坏一点,用户填入:

strSQL = "SELECT * FROM users;"
SQL语句变成了:

strSQL = "SELECT * FROM users WHERE name = 'any_value' and pw = ''; DROP TABLE users"

这样一来。虽然没有登录,可是数据表都被删除了。

然而使用PreparedStatement的參数化的查询能够阻止大部分的SQL注入。

在使用參数化查询的情况下,数据库系统(eg:MySQL)不会将參数的内容视为SQL指令的一部分来处理,而是在数据库完毕SQL指令的编译后。才套用參数运行。因此就算參数中含有破坏性的指令,也不会被数据库所运行。

补充:避免SQL注入的另外一种方式:
在组合SQL字符串的时候,先对所传入的參数做字符代替(将单引號字符代替为连续2个单引號字符,由于连续2个单引號字符在SQL数据库中会视为字符中的一个单引號字符,譬如:

strSQL = "SELECT * FROM users WHERE name = '" + userName + "';"
传入字符串:
userName  = " 1' OR 1=1 "
把userName做字符替换后变成:
userName = " 1'' OR 1=1"
最后生成的SQL查询语句为:

strSQL = "SELECT * FROM users WHERE name = '1'' OR 1=1'

这样数据库就会去系统查找name为“1′ ‘ OR 1=1”的记录。而避免了SQL注入。

比起凌乱的字符串追加似的查询,PreparedStatement查询可读性更好、更安全。

PreparedStatement的局限性

虽然PreparedStatement很有用。可是它仍有一定的限制。
1. 为了防止SQL注入攻击。PreparedStatement不同意一个占位符(?)有多个值。在运行有**IN**子句查询的时候这个问题变得棘手起来。以下这个SQL查询使用PreparedStatement就不会返回不论什么结果

SELECT * FROM loan WHERE loan_type IN (?

)
preparedSatement.setString(1, "'personal loan', 'home loan', 'gold loan'");
那怎样解决问题呢?请你继续关注本博客。下期告诉你答案。

不算总结的总结
关于PreparedStatement接口,须要重点记住的是:
1. PreparedStatement能够写參数化查询,比Statement能获得更好的性能。
2. 对于PreparedStatement来说,数据库能够使用已经编译过及定义好的运行计划。这样的预处理语句查询比普通的查询运行速度更快。

3. PreparedStatement能够阻止常见的SQL注入式攻击。
4. PreparedStatement能够写动态查询语句
5. PreparedStatement与java.sql.Connection对象是关联的,一旦你关闭了connection,PreparedStatement也没法使用了。

6. “?” 叫做占位符。

7. PreparedStatement查询默认返回FORWARD_ONLY的ResultSet,你仅仅能往一个方向移动结果集的游标。

当然你还能够设定为其它类型的值如:”CONCUR_READ_ONLY”。
8. 不支持预编译SQL查询的JDBC驱动,在调用connection.prepareStatement(sql)的时候。它不会把SQL查询语句发送给数据库做预处理,而是等到运行查询动作的时候(调用executeQuery()方法时)才把查询语句发送个数据库,这样的情况和使用Statement是一样的。
9. 占位符的索引位置从1開始而不是0,假设填入0会导致*java.sql.SQLException invalid column index*异常。

所以假设PreparedStatement有两个占位符。那么第一个參数的索引时1,第二个參数的索引是2.

以上就是为什么要使用PreparedStatement的所有理由,只是你仍然能够使用Statement对象用来做做測试。可是在生产环境下你一定要考虑使用 PreparedStatement 。

PreparedStatement与Statement的更多相关文章

  1. Java中PreparedStatement与Statement的总结

    概要: PreparedStatement 接口继承自 Statement 接口,PreparedStatement 比普通Statement 对象使用起来更加灵活,更有效率. 一.PreparedS ...

  2. JDBC增删改查,PreparedStatement和Statement的区别

    此篇是在上一篇的基础上使用PreparedStatement对象来实现JDBC增删改查的 具体工具类JDBCTools和实现类和配置文件在上一篇Statement对象实现的时候有写. 上一篇地址htt ...

  3. PreparedStatement与Statement的区别

    PreparedStatement与statement的区别 1.PreparedStatement是预编译的,对于批量处理可以大大提高效率. 也叫JDBC存储过程 2.使用 Statement 对象 ...

  4. preparedStatement和Statement 有什么不一样

    1. PreparedStatement接口继承Statement, PreparedStatement 实例包含已编译的 SQL 语句,所以其执行速度要快于 Statement 对象.    2.作 ...

  5. PreparedStatement和Statement的区别

    转自:http://blog.sina.com.cn/s/blog_77eba18f01019csh.html 1. PreparedStatement接口继承Statement, PreparedS ...

  6. 应该始终以PreparedStatement代替Statement

    在JDBC应用中,如果你已经是稍有水平开发者,你就应该始终以PreparedStatement代替Statement.也就是说,在任何时候都不要使用Statement 一.代码的可读性和可维护性.虽然 ...

  7. Java中PreparedStatement和Statement的用法区别(转)

    1. PreparedStatement接口继承Statement, PreparedStatement 实例包含已编译的 SQL 语句,所以其执行速度要快于 Statement 对象.   2.作为 ...

  8. JDBC中PreparedStatement和Statement的区别

    共同点: PreparedStatement和Statement都是用来执行SQL查询语句的API之一. 不同点: 在PreparedStatement中,当我们经常需要反复执行一条结构相似的sql语 ...

  9. 在JDBC中使用PreparedStatement代替Statement,同时预防SQL注入

    本篇讲诉为何在JDBC操作数据库的过程中,要使用PreparedStatement对象来代替Statement对象. 在前面的JDBC学习中,对于Statement对象,我们已经知道是封装SQL语句并 ...

随机推荐

  1. javaee加密部署,tomcat使用自己的classloader解密【正解】

    [起因] 公司需要对一个web项目进行加密之后出售, 大家都知道的,class很好反编译, 所以需要对class文件先进行加密, 然后使用自己的classloader进行解密并加载. [步骤] 大概分 ...

  2. Qt的setMouseTracking使用

    bool mouseTracking 这个属性保存的是窗口部件跟踪鼠标是否生效. 如果鼠标跟踪失效(默认),当鼠标被移动的时候只有在至少一个鼠标按键被按下时,这个窗口部件才会接收鼠标移动事件. 如果鼠 ...

  3. 1.0.x-学习Opencv与MFC混合编程之---视频运动检测

    源代码地址: http://download.csdn.net/detail/nuptboyzhb/3961668 版本1.0.x新增内容 视频运动检测 Ø 新建菜单项,Learning OpenCV ...

  4. Possible concurrency problem: Replicated version id X matches in-memory version for session ...

    The message basically is saying that a replicated session is overriding an existing session in that ...

  5. robotframework ride 版本兼容问题

    在安装robotFramework ride的时候,必须需要使用wxpython 目前使用的wxpython 还必须是unicode 版本的要不然不支持中文 目前使用的 wx.version.2.8. ...

  6. jvm常用参数设置 good

    1.堆的大小可以通过 -Xms 和 -Xmx 来设置,一般将他们设置为相同的大小,目的是避免在每次垃圾回收后重新调整堆的大小,比如 -Xms=2g -Xmx=2g 或者 -Xms=512m -Xmx= ...

  7. perl .*?和.*

    redis01:/root# cat x2.pl my $str="212121a19823a456123"; if ($str =~/.*a(.*)23/){print &quo ...

  8. hdu 4507 数位dp(求和,求平方和)

    http://acm.hdu.edu.cn/showproblem.php?pid=4507 Problem Description 单身! 依旧单身! 吉哥依旧单身! DS级码农吉哥依旧单身! 所以 ...

  9. RGB HSV HLS三种色彩模式转换(C语言实现)

    Android项目上处理图像的代码(注释全部去掉) ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 ...

  10. 让MFC程序隐藏运行界面

    在MFC中隐藏运行界面确实花花点功力. 针对对话框程序,一种不是很好地实现方法是在OnPaint函数中添加如下代码: CWnd::ShowWindow(SW_HIDE); 添加后执行会发现屏幕会闪烁一 ...