目录
1.Mybatis概述
Mybatis是一个内部封装了JDBC的框架,主要用于数据持久化,也就是管理数据库操作语句.在对SQL语句做一定的操作处理后,将sql提交到数据库(Mysql),可以使用#{}和${}进行部分值的传递,但这两者有区别.
2.问题语句示例:
在xml文件中有如下语句(伪代码,主要是sql语句)
#语句一
<select >
select id,name,password from user where id=#{id}
</select>
#语句二
<select>
select id,name,password from user where id=${id}
</select>
上述两条语句执行都会成功返回数据.(其中id是数值型数据,例如int,long).
而在如下代码中
#语句一
<select>
select id,name,password from user where name=#{name}
</select>
#语句二
<select>
select id,name,password from user where name=${name}
</select>
语句一会执行成功,而语句二则会执行报错,且报错语句为 1054 - Unknown column '李泽天' in 'where clause' ('李泽天'为name字段的某一个值).其中name为字符类型数据,.
3.语句问题分析:
在SQL语句中,除了关键词、数值、运算符、函数名等固定的内容以外,所有内容都会被视为某个名字,例如:表名、字段名或其它自定义的名称(例如表名.索引名等).第二部分语句的报错就是因为传过去sql的字段name为字符类型,导致 '李泽天' 被作为了字段名进行解析,由于user表中没有这个字段所以进行了报错.对于${}传递的数值类型的id值,则不会被视为某个名字,因此不会有问题.
4.#{}与${}底层实现区别分析
之所以会上述代码段会出现差别,底层原因是因为mysql对#{}与${}的处理不同.MySQL处理SQL语句时,会经过词法分析、语义分析,然后再执行编译,最后执行.
在Mybatis中,使用#{}
格式的占位符时,使用的 JDBC 对象是PreparedStatement 对象. 在底层实现时,使用预编译的处理机制,先对传递的值的位置进行占位,位置中的东西不会经过编译处理,只会被认作是值,在sql执行时再代入!由于采用了预编译的做法,所有值都不用考虑数据类型的问题,只能表示SQL语句中的某个值,而不能表示其它内容!预编译是安全的,不会出现SQL注入问题!
使用${}
格式的占位符时,使用的 JDBC 对象是 Statement对象..在底层实现时 ,会先将值拼接到原SQL语句中,然后再执行词法分析、语义分析,然后再执行编译,最后执行. 由于不是预编译的,所以可以表示SQL语句中的任何片段,只需要保证拼接起来后可以通过编译即可 , 但除了数值类型,其他数据类型都需要用 单引号' ' 框起来(需要注意的是,单引号的意思并不是表明引号中的内容为字符串,而是起到一个占位的作用,占位的区域内容不参与编译,,而只在执行时带入). 由于是先代入值再执行编译相关流程,所以,代入的值是可以改变语义的,是存在SQL注入风险的!
5.总结区别:
#{} :
- 使用的 JDBC 对象是PreparedStatement,
- 经过预编译处理
- 只能传递值
- 安全,不会出现SQL注入风险
${}:
- 使用的JDBC对象是 Statement对象.
- 需要完整经过SQL语句处理流程
- 可以传递任意部分内容
- 不安全,存在SQL注入风险