• Statement和PreparedStatement深入学习总结


    最近在看java安全编码方面的书籍,在看到SQL注入漏洞的问题时,引发了我对Statement和PreparedStatement深入总结的欲望,废话少说,下面咱们就正式开始。

    当初始的SQL查询被修改成另一个完全不同形式的查询的时候,就会出现SQL注入漏洞。执行这一被修改过的查询就可能会导致信息泄露或者数据被修改。防止SQL注入漏洞的主要方法是:净化并验证非受信输入,同时采用参数化查询的方法。

           //创建数据库连接的过程略
            String sql="select * from db_user where username = " + username + "and passord = " + password;
            Statement stmt = connect.createStatement();
            ResultSet rs = stmt.executeQuery(sql);
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    采用以上方法实现数据查询时,如果攻击者能够替代username和password中的任意字符串,它们可以使用类似下面这种方式实现SQL注入:

    select * from db_user where username = '随意' and password = '' or '1' = '1';
    • 1

    因为’1’=’1’肯定成立,所以可以任何通过验证.更有甚者:把[‘;drop table tb_name;]作为password传入进来,则:

    select * from db_user where username = '随意' and password = '';drop table tb_name;
    • 1

    有些数据库是不会让你成功的,但也有很多数据库就可以使这些语句得到执行。而如果你使用预编译语句。你传入的任何内容就不会和原来的语句发生任何匹配的关系。(前提是数据库本身支持预编译,但上前可能没有什么服务端数据库不支持编译了,只有少数的桌面数据库,就是直接文件访问的那些)只要全使用预编译语句,你就用不着对传入的数据做任何过滤。而如果使用普通的statement,有可能要对drop,等做费尽心机的判断和过滤。

    改进方法:

            //创建数据库连接的过程略
            String sql="select * from db_user where username = ? and passord = ?";
            PreparedStatement stmt = connect.preparedStatement();
            stmt.setString(1,username);
            stme.setString(2,password);
            ResultSet rs = stmt.executeQuery(sql);
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    通过使用PreparedStatement类的set*()方法,可以进行强类型检查。这样可以减少SQL注入漏洞。

    一、分情况使用Statement和PreparedStatement对象

    JDBC驱动的最佳化是基于使用的是什么功能. 选择PreparedStatement还是Statement取决于你要怎么使用它们. 对于只执行一次的SQL语句选择Statement是最好的. 相反, 如果SQL语句被多次执行选用PreparedStatement是最好的.

    PreparedStatement的第一次执行消耗是很高的. 它的性能体现在后面的重复执行. 使用PreparedStatement的方式来执行一个针对数据库表的查询. JDBC驱动会发送一个网络请求到数据解析和优化这个查询. 而执行时会产生另一个网络请求. 在JDBC驱动中,减少网络通讯是最终的目的. 如果我的程序在运行期间只需要一次请求, 那么就使用Statement. 对于Statement, 同一个查询只会产生一次网络到数据库的通讯.

    对于使用PreparedStatement池的情况下,当使用PreparedStatement池时, 如果一个查询很特殊, 并且不太会再次执行到, 那么可以使用Statement. 如果一个查询很少会被执行,但连接池中的Statement池可能被再次执行, 那么请使用PreparedStatement. 在不是Statement池的同样情况下, 请使用Statement.

    二、PreparedStatement的Batch功能

    Update大量的数据时, 先构建一个INSERT语句再多次的执行, 会导致很多次的网络连接.。要减少JDBC的调用次数改善性能, 可以使用PreparedStatement的AddBatch()方法一次性发送多个查询给数据库。

    初始实现:
    
    PreparedStatement ps = conn.prepareStatement(  
       "INSERT into db_user values (?, ?, ?)");  
    
    for (n = 0; n < 100; n++) {  
    
      ps.setString(name[n]);  
      ps.setLong(id[n]);  
      ps.setInt(salary[n]);  
      ps.executeUpdate();  
    }  
    
    改进实现:
    
    //使用Batch功能
    PreparedStatement ps = conn.prepareStatement(  
       "INSERT into db_user values (?, ?, ?)");  
    
    for (n = 0; n < 100; n++) {  
    
      ps.setString(username[n]);  
      ps.setString(password[n]);   
      ps.addBatch();  
    }  
    ps.executeBatch();
    • 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

    在初始实现中, PreparedStatement被用来多次执行INSERT语句。 在这里,执行了100次INSERT操作, 共有101次网络往返。 其中,1次往返是预储statement, 另外100次往返执行每个迭代。 在改进实现中, 当在100次INSERT操作中使用addBatch()方法时, 只有两次网络往返。 1次往返是预储statement, 另一次是执行batch命令。虽然Batch命令会用到更多的数据库的CPU周期, 但是通过减少网络往返,性能得到提高。 记住, JDBC的性能最大的增进是减少JDBC驱动与数据库之间的网络通讯。

    注:Oracel 10G的JDBC Driver限制最大Batch size是16383条,如果addBatch超过这个限制,那么executeBatch时就会出现“无效的批值”(Invalid Batch Value) 异常。因此在如果使用的是Oracle10G,在此bug减少前,Batch size需要控制在一定的限度。

  • 相关阅读:
    提示“Resource temporarily unavailable”的原因及解决办法
    TQ2440与西门子S7-200 PLC自由口通信实现过程中问题总结
    SQL与SQL Server
    JavaScript:事件对象Event和冒泡
    JavaScript动画:offset和匀速动画详解(含轮播图的实现)
    JavaScript基础:BOM的常见内置方法和内置对象
    JavaScript实现Tab栏切换
    JavaScript基础:DOM操作详解
    JavaScript语法基础:数组的常用方法详解
    JavaScript语法详解:if语句&for循环&函数
  • 原文地址:https://www.cnblogs.com/firstdream/p/7689345.html
Copyright © 2020-2023  润新知