关于PreparedStatement和Statement
0x01 不同
看了一些网上的博客,主要结论有以下三点:
Statement | PreparedStatement | |
---|---|---|
可读性 | 差,需要拼接字符串 | 高,可单独设置每个变量 |
预编译 | 普通sql无预编译 | 可配置开启预编译 |
安全性 | 差,可能被sql注入到拼接字段 | 强,sql提前预编译,传入的参数中的字符串,如果有特殊字符会被转义 |
0x02 开启预编译
很多主流持久层框架(MyBatis,Hibernate)其实都没有真正的用上预编译,预编译是要我们自己在参数列表上面配置的,如果我们不手动开启,JDBC驱动程序5.0.5以后版本 默认预编译都是关闭的。
所以我们要在参数列表中配置,例如:
jdbc:mysql://localhost:3306/mybatis?&useServerPrepStmts=true&cachePrepStmts=true
- useServerPrepStmts
表示mysql驱动会先把SQL语句发送给服务器进行预编译,然后在执行executeQuery()时只是把参数发送给服务器 - cachePrepStmts
当使用不同的PreparedStatement对象来执行相同的SQL语句时,还是会出现编译两次的现象,这是因为驱动没有缓存编译后的函数key,导致二次编译。如果希望缓存编译后函数的key,那么就要设置cachePrepStmts参数为true
0x03 PreparedStatement
在Connector/J
有PreparedStatementd的两种实现即客户端侧和服务端的预编译statements:
- 客户端侧的PreparedStatement是默认选项。因为早期MySQL版本不支持预编译或实现有问题。
- 服务端侧的PreparedStatement和二进制编码的result set一般是在服务端支持时启用,启用命令是
useServerPrepStmts=true
当使用服务端侧的PreparedStatement且参数是用setBinaryStream(), setAsciiStream(), setUnicodeStream(), setCharacterStream(), setNCharacterStream(), setBlob(), setClob(), or setNCLob()
设置时一定要特别注意。
重新执行statement,且参数由大参数调整为小参数是,使用clearParameters()
方法来重设所有参数。原因如下:
-
在服务器端预处理语句和客户端仿真期间,只有在调用PreparedStatement.execute()方法时才会交换大数据。
-
一旦这个过程完成,被用来读取客户端侧数据的流被关闭(根据JDBC规范),而且不能再次读取。
-
如果一个参数从大转为小,
driver
就必须重设服务端侧的PreparedStatement状态来允许正在被修改的参数替代之前的大值。这样会移除所有已经被发送到服务端的大型数据,因此需要使用前面讲到的setBinaryStream(), setAsciiStream(), setUnicodeStream(), setCharacterStream(), setNCharacterStream(), setBlob(), setClob(), or setNCLob()
方法来重发数据。
0x04 Statement
当使用3.2.1
以前版本的JDBC driver连接5.0.3
以前版本的myslq 服务时,setFetchSize()
方法不生效,
0xFF 参考文档
MySQL Connector/J 5.1 Developer Guide
JDBC中的——PreparedStatement 预编译原理
PreparedStatement的原理
JDBC:深入理解PreparedStatement和Statement
你可能需要重新理解JDBC PreparedStatement
以mysql为例介绍PreparedStatement防止sql注入原理