问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501

JDBC为什么要使用PreparedStatement而不是Statement

发布网友 发布时间:2022-04-21 17:59

我来回答

2个回答

懂视网 时间:2022-04-29 23:24

一、前言

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对象生成的查询速度更快。

    下面是一个例子:

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

     

    这个例子中,如果还是用 PreparedStatement 做同样的查询,哪怕参数值不一样,比如:”Standard Chated” 或者”HSBC”作为参数值,数据库系统还是会去调用之前编译器编译好的执行语句(系统库系统初次会对查询语句做最大的性能优化)。

    默认会返回”TYPE_FORWARD_ONLY”类型的结果集( ResultSet ),当然你也可以使用preparedstatment()的重载方法返回不同类型的结果集。

    三、预处理语句的优势

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

    1)PreparedStatement可以写动态参数化的查询

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

    SELECT interest_rate FROM loan WHERE loan_type=?

    现在你可以使用任何一种loan类型如:”personal loan”,”home loan” 或者”gold loan”来查询,这个例子叫做参数化查询,因为它可以用不同的参数调用它,这里的”?”就是参数的占位符。 

    2)PreparedStatement比 Statement 更快

    使用 PreparedStatement 最重要的一点好处是它拥有更佳的性能优势,SQL语句会预编译在数据库系统中。执行计划同样会被缓存起来,它允许数据库做参数化查询。使用预处理语句比普通的查询更快,因为它做的工作更少(数据库对SQL语句的分析,编译,优化已经在第一次查询前完成了)。为了减少数据库的负载,生产环境中德JDBC代码你应该总是使用PreparedStatement 。值得注意的一点是:为了获得性能上的优势,应该使用参数化sql查询而不是字符串追加的方式。下面两个SELECT 查询,第一个SELECT查询就没有任何性能优势。

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

    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能获得更好的性能。 

    3)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 = "1‘ OR ‘1‘=‘1";DROP TABLE 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‘

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

    四、PreparedStatement的局限性


    尽管PreparedStatement非常实用,但是它仍有一定的限制。 
    1. 为了防止SQL注入攻击,PreparedStatement不允许一个占位符(?)有多个值,在执行有**IN**子句查询的时候这个问题变得棘手起来。下面这个SQL查询使用PreparedStatement就不会返回任何结果 

    五、不算总结的总结

    关于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.

    热心网友 时间:2022-04-29 20:32

    PreparedStatement是用来执行SQL查询语句的API之一,Java提供了 Statement、PreparedStatement 和 CallableStatement三种方式来执行查询语句,其中 Statement 用于通用查询, PreparedStatement 用于执行参数化查询,而 CallableStatement则是用于存储过程。同时PreparedStatement还经常会在Java面试被提及,譬如:Statement与PreparedStatement的区别以及如何避免SQL注入式攻击
    PreparedStatement可以写动态参数化的查询
    用PreparedStatement你可以写带参数的sql查询语句,通过使用相同的sql语句和不同的参数值来做查询比创建一个不同的查询语句要好,下面是一个参数化查询:

    1

    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查询就没有任何性能优势。
    声明声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:11247931@qq.com
    深度解析:第一财经回放,探索财经新风向 逆水寒手游庄园怎么邀请好友同住 逆水寒手游 逆水寒不同区可以一起组队吗? 逆水寒手游 逆水寒怎么进入好友世界? 逆水寒手游 逆水寒怎么去别人的庄园? 使用puppeteer实现将htmll转成pdf 内卷时代下的前端技术-使用JavaScript在浏览器中生成PDF文档 【译】将HTML转为PDF的几种实现方案 变形金刚08动画怎么样 变形金刚08动画的问题 preparedStatement 用?代替参数时'?'附近有语法错... 如何理解PreparedStatementCache,以及如何使用 怎样退出银行卡自动转入余利宝 java中PreparedStatement与Statement相比具有什么... 余利宝如何解绑信用卡? PreparedStatement和Statement preparedstatement和preparestatement的区别 余利宝的网上银行卡怎么取消 JDBC中Statement和PrepareStatement的区别及特性 jdbc中,preparedstatement对象使用什么符号作为占位符 余利宝怎么关闭或者注销 余利宝帮攒钱银行卡怎么解除绑定? 抑郁症能预防么!? 这是抑郁症的表现吗? 为什么现在得抑郁症的人越来越多了?我国患抑郁症... 得抑郁症很久了 抑郁情绪需要看医生吗 这是不是抑郁症的表现,易怒 尤其是对身边亲近的人... 我父亲55岁,平时小心眼,不爱说话,最近发现好像... 什么叫抑郁症 支付宝里的银行卡已经解绑了为什么余利宝里还有银行卡 求大神,JSP的preparedStatement的setInt()方法报... jsp sql 中如何得到PreparedStatement类型的值? 华为北斗上市了吗? java mysql preparedstatement语句出错 怎么获取PreparedStatement 的最终执行SQL 余利宝转账后遗留的银行卡号码如何消除? jdbc模板占位符替换,处理结果集使用什么机制 JDBC中?占位符的使用说明 java sql in可以用占位符吗 jdbc 占位符插入 NullPointerException SQL Server中能否使用“?”占位符 JDBC中的PreparedStatement相比Statement的好处有... 吃苦瓜有哪些好处? 吃苦瓜的好处有哪些? 吃苦瓜有什么特别的好处? 吃苦瓜有什么好处吗? 吃苦瓜有那些好处呢? 常吃苦瓜有什么益处? 吃苦瓜有什么好