用过Mybatis的都应该了解 #{} ${} 的区别:一般较为常见的回答是:#{}是预编译处理,${}是字符串替换。
所以涉及到了 myabties的底层使用PreparedStatement。但是按照官方说法预编译功能是需要手动开启的。这里以mysql为例:MySQL启用预编译的先决条件是useServerPstmts=true。MySQL是否默认开启和jdbc的版本有很大关系,关系如下:
MySQL Server 4.1之前的版本是不支持预编译的
jdbc驱动是5.0.5之前的版本,默认开启了预编译
jdbc驱动是5.0.5之后的版本,需要手动开启了预编译
我现在使用的mysql是5.7版本,如何打开mysql的查询日志如下
执行
SET GLOBAL log_output=‘FILE’;//将操作记录到文件
SET GLOBAL general_log_file = ‘d:/log/mysql.txt’;日志保存位置
SET GLOBAL general_log = ‘ON’; //开启日志
查看结果
SHOW VARIABLES LIKE "general_log%"
结果如下
下面开始测试
因为我的jdbc版本比较高,所以是默认关闭的测试代码如下
Class.forName("com.mysql.cj.jdbc.Driver");
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost/monster?" +
"serverTimezone=UTC&characterEncoding=utf-8","root","root");
PreparedStatement ps = connection.prepareStatement("select * from adress where country = ?");
ps.setString(1,"中国");
ResultSet resultSet = ps.executeQuery();
while(resultSet.next()){
System.out.println(resultSet.getString("name"));
}
resultSet.close();
connection.close();
日志如下
很明显 并没有任何预编译的代码
然后我们修改jdbc连接串,显性开启预编译,代码如下
Class.forName("com.mysql.cj.jdbc.Driver");
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost/monster?" +
"serverTimezone=UTC&characterEncoding=utf-8&useServerPrepStmts=true&&cachePrepStmts=true","root","root");
PreparedStatement ps = connection.prepareStatement("select * from adress where country = ?");
ps.setString(1,"中国");
ResultSet resultSet = ps.executeQuery();
while(resultSet.next()){
System.out.println(resultSet.getString("name"));
}
resultSet.close();
connection.close();
日志如下:
可以看到有预编译的操作
故而我们查看jdbc代码可知
真正的预编译,是需要参数支持的。
所以不能说 PreparedStatement就是预编译。那么问题来了,如果PreparedStatement不是预编译,那么他是如何防止sql注入的呢,我做了一些简单的sql注入测试,日志如下,代码略
根据这个特点我们追踪到
ps.setString(1,“中国”);代码,他到底做了什么操作,代码如下
F7 进入可知
他实际上是做了 两端+单引号处理 来防止sql注入的,并不是预编译的效果,使用?作为占位符替换。
PreparedStatement不是将参数简单拼凑成sql,而是做了一些预处理,将参数转换为string,两端加单引号,将参数内的特殊字符(换行,单双引号,斜杠等)做转义处理,很大限度的避免了sql注入。
所以结论是
在未开启预编译参数时,实际上是字符串处理,打开预编译参数,才是真正的预编译
回到开头,mybaties #{} ${}区别是什么,下面才是比较正确的回答
’#‘相当于对数据 加上 双引号,在未开启预编译参数的情况下,其实是伪处理 ,’$'相当于直接显示数据 。
如果面试谈到预编译问题,你可以说需要开启预编译参数才是真正的预编译,否则都是PreparedStatement的伪处理。
以上都是个人简介如果不对 请斧正 谢谢