前言
动态SQL是MyBatis的强大特性之一,我们在使用MyBatis作为持久层框架时,经常需要动态传递参数,例如我们需要根据用户的姓名来筛选用户时,SQL如下:1
select * from user where name = "Lihua";
上述SQL中,我们希望name
是动态可变的,即不同的时刻根据不同的姓名来查询用户,那么在MyBatis的xml中可以如下配置:1
select * from user where name = #{name};
或者1
select * from user where name = '${name}';
这两种方式的本质是不同的,如果不了解其原理,在某些场景下会导致意想不到的后果。
区别
MyBatis在对SQL语句进行预编译之前,会对SQL进行动态解析,解析为一个BoundSql
对象,也正是在这个阶段#{}
和${}
会有不同的表现。
#{}
在动态SQL解析阶段,#{}
解析为一个JDBC预编译(PreparedStatement
)的参数标记符。例如,如下的SQL语句:1
select * from user where name = #{name};
会被解析为:1
select * from user where name = ?;
也就是说,一个#{}
被解析为一个参数占位符?
。
${}
在动态SQL解析阶段,${}
仅仅为一个纯粹的字符串替换。例如,如下的SQL语句:1
select * from user where name = '${name}';
当我们传递的参数为Lihua
时,上述SQL解析为:1
select * from user where name = "Lihua";
也就是说,预编译前的SQL语句已经不包含变量name
了。
总结
对于#{}
,它其实就对应着JDBC中的PreparedStatement
的?
,因此一旦MySQL服务器对SQL模板进行了编译,并且存储了函数,PreparedStatement
做的就是把参数进行转义后直接传入参数到数据库,然后让函数执行,也就避免了SQL注入的问题;而${}
的变量替换是在动态SQL解析阶段,也就是预编译之前,相当于这个SQL语句已经是个常量了,因此会产生SQL注入的问题。