为什么用preparedStatement 而不是statement

尽管自己写代码用了preparedstatement, 但是对于它和statement 之间的区别不是非常了解。 正好今天有时间学习实践一下, 尤其是sql 注入。

1 代码实践

首先比较一下statement 和preparedstatement 的调用方式, 后者的调用代码量稍多一些 -网上找的

Statement:

stmt.executeUpdate("insert into tb_name (col1,col2,col2,col4) values ('"+var1+"','"+var2+"',"+var3+",'"+var4+"')");

preparedStatement:

perstmt = con.prepareStatement("insert into tb_name (col1,col2,col2,col4) values (?,?,?,?)");
perstmt.setString(1,var1);
perstmt.setString(2,var2);
perstmt.setString(3,var3);
perstmt.setString(4,var4);
perstmt.executeUpdate();


2 preparedSatement 的优点

1) 允许动态和参数化的query; 易于维护


2) 运行效率快

其实就是预编译的问题。 PreparedSatement 会预编译, 而Statement基本上无法重用。 所以多次执行的话前者效率高很多。 生产环境中遇到过这种情况, 有几万条sql, 数据库的工作效率严重下降, 后来通过替换成PreparedStatement解决了这个问题

每一种数据库都会尽最大努力对预编译语句提供最大的性能优化.因为预编译语句有可能被重复调用.所以语句在被DB的编译器编译后的执行代码被缓存下来,那 么下次调用时只要是相同的预编译语句就不需要编译,只要将参数直接传入编译过的语句执行代码中(相当于一个涵数)就会得到执行.这并不是说只有一个 Connection中多次执行的预编译语句被缓存,而是对于整个DB中,只要预编译的语句语法和缓存中匹配.那么在任何时候就可以不需要再次编译而可以 直接执行.

而statement的语句中,即使是相同一操作,而由于每次操作的数据不同所以使整个语句相匹配的机会极小,几乎不太可能匹配.比如:

insert into tb_name (col1,col2) values ('11','22');
insert into tb_name (col1,col2) values ('11','23');

即使是相同操作但因为数据内容不一样,所以整个个语句本身不能匹配,没有缓存语句的意义.事实是没有数据库会对普通语句编译后的执行代码缓存.

当然并不是所以预编译语句都一定会被缓存,数据库本身会用一种策略,比如使用频度等因素来决定什么时候不再缓存已有的预编译结果.以保存有更多的空间存储新的预编译语句.

3) 安全性 预防sql注入的攻击

开始没有明白这点, 后来从stackflow 查到一个例子,就很容易理解了。

Statement stmt = conn.createStatement("INSERT INTO students VALUES('" + user + "')");
stmt.execute();

Or

Statement stmt = conn.prepareStatement("INSERT INTO student VALUES(?)");
stmt.setString(1, user);
stmt.execute();

If "user" came from user input and the user input was

Robert'); DROP TABLE students; --

然后自己写了代码实践了一下, 发现确实如果statement 会把 table drop掉。 而prepared的呢因为已经预编译了, 根本就不会执行这个drop语句。 所以是安全的。 

3 实例

如何使用以及如何测试依赖注入, 这里是本地 postgres 数据库, 用户名密码postgres postgres

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class PreparedStmtExample {
    public static void main(String args[]) throws SQLException {
        System.out.println("-------- PostgreSQL " + "JDBC Connection Testing ------------");
        try {
            Class.forName("org.postgresql.Driver");
        } catch (ClassNotFoundException e) {
            System.out.println("Where is your PostgreSQL JDBC Driver? " + "Include in your library path!");
            e.printStackTrace();
            return;
        }
        System.out.println("PostgreSQL JDBC Driver Registered!");
        // statement
        Connection conn = DriverManager.getConnection( "jdbc:postgresql://127.0.0.1:5432/postgres", "postgres", "postgres");
//        String name = "'name'";
        String name1 = "'name'; drop table table1;";
        Statement stmt = conn.createStatement();
//        ResultSet result = stmt
//                .executeQuery("select * from network where id="
//                        + name1);
        ResultSet result = stmt
                .executeQuery("select * from network where id="
                        + name1);
        
        while (result.next()) {
            System.out.println("Loan Type: " + result.getString("sid"));
        }
        // preparedstatement
         Connection conn1 =
         DriverManager.getConnection("jdbc:postgresql://127.0.0.1:5432/postgres", "postgres", "postgres");
         PreparedStatement preStatement = conn1 .prepareStatement("select * from network where id=?");
         preStatement.setString(1, "name; drop table table1;");
        
         ResultSet result1 = preStatement.executeQuery();
        
         while (result1.next()) {
         System.out.println("Loan Type: " + result1.getString("sid"));
         }
    }
}

要预先create 一个table :network, column id,sid; 加一个record name;xxx 即可。
    create 第二个table: table1

4 结论与其他

这次主要理解了如何使用,以及如何防止sql注入。 以后一般就直接用preparedStatement 即可。

Next

1) 关于sql 执行过程原理是否可以理解一下

5 参考

1)Why use PreparedStatement in Java JDBC – Example Tutorial

http://javarevisited.blogspot.com/2012/03/why-use-preparedstatement-in-java-jdbc.html




你可能感兴趣的:(为什么用preparedStatement 而不是statement)