【8015】知识点背景:在 mapper 中定义的参数通过${}和#{}两种方式传到 xml 中之后,在查询之前 mybatis 会对其进行动态解析;
问题背景:
今天下午练习Mybatis项目,在mappper.xml编写动态sql语句时用${}替换#{}配参,结果出现报错,在我印象里${}也是能够进行参数配置的,但是运行后却报出java.sql.SQLSyntaxErrorException: Unknown column ‘XXX‘ in ‘where clause,于是就去查找一下关于${}与#{}的异同,在这里总结一下相关知识点以及问题解决方法。
解决方法:
方法一:
在mappper.xml编写sql语句时,给配参加上单引号 ''即可,如:
select * from user where username like '${username}'
通过控制台(log4j日志可以但打印出执行的sql语句,没有的可以去下载安装一个,很实用的)的报错信息可以看出,当前执行的sql语句为:
select * from user where username like zhangsan
显然这是一个错误的sql语句,参数为字符串,执行的参数却少了单引号,解决方法只需在传递参数的时候加上单引号 ''即可。
方法二:
通过方法一里的分析,我们知道,如果是字符串类型,${}传递的参数是不加单引号的,给什么传递什么,执行sql又需要单引号,那么我们直接在参数上单引号 ''同样也能达到效果,如:
List<User> users = userMapper.selectLike2("'zhangsan'");
原因分析:
1.当#{}传入的数据是一个字符串时,它会将#{}中的参数替换为"?",然后会根据对应的类型做参数字符串类型替换,会自动加上单引号 ''有学过JDBC的,感觉有些像preparestatement预编译的功能,而${}更多像statement编译功能(这点个人猜测,仅参考)。如:
select * from user where username like ?
select * from user where username like 'zhangsan'
当然对于#{}传入的数据不仅仅是字符串会根据对应的类型做参数字符串类型替换,其他类型也同样如此,即使在集合也可能类型转换:如:整形
2.当${}进行传入一个字符串时,会直接显示生成在sql中,其他类型也是如此,没有类型转换这一说,关于这点有缺点,最直接的是无法防止sql注入,当然你如果在前端就处理好了,那当我没说,只需将字符串类型参数加上单引号、整形本来就不需要变等,效果和#{}其实没啥差别,就是写着麻烦,所以${}常常用在不加单引号的传参中,如:表名就不需要加单引号、order by 动态参数时不加、jdbc.properties静态文本替换${jdbcDriver}等等;
3.最后,考虑到安全性sql注入的问题,常常使用#{},而不用${},当然具体的问题具体分析,需要使用到不加单引号的情况,就需要用${}了。
4.关于${}与#{}的知识点,目前就了解这么多,想更深入的了解,可以去找找mybatis对其的设计理念已经运行源码,欢迎大佬底下评论,以后发现不足,会及时回来补充,嘿嘿,加油!
在Mybatis面试中常涉及到关于#{}和${}的区别:(又回来补充的,找资料看到一个不错的博主总结)
有需要的可以去了解一下:
https://blog.csdn.net/weixin_45393094/article/details/113953821
4. ${ } 变量的替换阶段是在动态 SQL 解析阶段,而 #{ }变量的替换是在 DBMS 中;(这和我上边的猜测好像,可以看出我那个猜测基本没错)