目录
Statement和PreparedStatement在使用时如何选择
我们可以通过调用 Connection 对象的 preparedStatement(String sql) 方法获取 PreparedStatement 对象 ,PreparedStatement 接口是 Statement 的子接口,他会将SQL语句在创建对象是就预编译, PreparedStatement 对象所代表的 SQL 语句中的参数可以用占位符(?)来替换,随后调用 PreparedStatement 对象的setXxx() 方法可以设置这些参数. setXxx() 方法有两个参数,第一个参数是要设置的 SQL 语句中的参数的索引(从 1开始),第二个是设置的 SQL 语句中的参数的值;
preparedStatement 有三大优点:
当SQL语句格式固定的时我们倾向于使用PreparedStatement;PreparedStatement预编译就是将SQL语句的格式告诉虚拟机,我要执行的是什么操作,让虚拟机知道他要干什么,例如:
String sql1="select id ,name,age from emp where id=?";
预编译功能举例:将上面的sql1传递给preparedStatement,preparedStatement的预编译就是,告诉虚拟机我接下来的操作是要在表名为Emp的表格中通过ID查询name,age等信息,你就去查询就得了;
我们先看看什么怎样就会形成SQL注入,看下面代码,下面语句会造成SQL注入问题
SQL = “SELECT * FROM users WHERE name = ‘1’ OR ‘1’=‘1’ and pw = ‘1’ OR ‘1’=‘1’;”
编译器会将如下代码认为是这样的:
SQL = "SELECT * FROM users";
为什么会这样呢?系统会将where条件看成(条件1and条件2),其中判断语句『name = '1' OR '1'='1'』与『pw = '1' OR '1'='1'』的结果都恒为True,那么实际系统执行的语句为:因为语句中的WHERE条件恒为真,这就相当于执行,也就是说你尽管没有账号和密码课可以进入数据库并操作数据;
然而preparedStatement会通过被占位符替换的代码,提前告知系统我这里进行的是通过填入自己的用户名(userName )和密码之后(passWord ),查询其用户名与密码是否对应来判断登录成功与否,不会在看成条件1and条件2这样的。这样就避免了SQL注入问题;
sql语句在被DB的编译器编译后的执行代码被缓存下来,那么下次调用时只要是相同的预编译语句就不需要编译,只要将参数直接传入编译过的语句执行代码中(相当于一个涵数)就会得到执行.这并不是说只有一个Connection中多次执行的预编译语句被缓存,而是对于整个DB中,只要预编译的语句语法和缓存中匹配.那么在任何时候就可以不需要再次编译而可以直接执行.而statement的语句中,即使是相同一操作,而由于每次操作的数据不同所以使整个语句相匹配的机会极小,几乎不太可能匹配。
选择PreparedStatement还是Statement取决于你要怎么使用它们. 对于只执行一次的SQL语句选择Statement是最好的. 相反, 如果SQL语句被多次执行选用PreparedStatement是最好的.
PreparedStatement的第一次执行消耗是很高的. 它的性能体现在后面的重复执行. 例如, 假设我使用Employee ID, 使用prepared的方式来执行一个针对Employee表的查询. JDBC驱动会发送一个网络请求到数据解析和优化这个查询. 而执行时会产生另一个网络请求.在JDBC驱动中,减少网络通讯是最终的目的. 如果我的程序在运行期间只需要一次请求, 那么就使用Statement. 对于Statement, 同一个查询只会产生一次网络到数据库的通讯.
对于使用PreparedStatement池的情况下, 本指导原则有点复杂. 当使用PreparedStatement池时, 如果一个查询很特殊, 并且不太会再次执行到, 那么可以使用Statement. 如果一个查询很少会被执行,但连接池中的Statement池可能被再次执行, 那么请使用PreparedStatement. 在不是Statement池的同样情况下, 请使用Statement.