一. 什么是MyBatis
MyBatis是一款优秀的持久层框架,特别是在国内(国外据说还是 Hibernate 的天下)非常的流行,我们常说的SSM组合中的M指的就是MyBatis。
MyBatis支持定制化SQL、存储过程以及高级映射等多种特性,单纯从代码上来看,MyBatis避免了几乎所有的JDBC代码和手动设置参数以及手动处理结果集。而且MyBatis的使用也非常方便,可以通过简单的XML或注解来配置和映射数据信息。
对MyBatis不熟悉的小伙伴,可以关注公众号,回复【MyBatis】直接领取学习哦~
Java架构栈
专注分享Java技术干货,包括多线程、JVM、Spring Boot、Spring Cloud、Intellij IDEA、Dubbo、Zookeeper、Redis、架构设计、微服务、消息队列、Git、面试题、最新动态等。
252篇原创内容
公众号
二. 什么是动态 SQL
MyBatis的强大特性之一便是它的动态SQL,凡是使用过JDBC或其他类似框架,如DBUtils、JdbcTemplate等数据库操作工具的同学,都能体会到根据不同条件拼接SQL语句是有多么的痛苦!
我们不仅要拼接SQL,还要拼接参数,特别费事,而且拼接的时候还要确保不能忘了必要的空格,还要注意省略掉列名列表最后的逗号.......操作过这样代码的小伙伴应该都明白这里的弊端。然而利用MyBatis的动态SQL特性,就可以彻底摆脱这种痛苦。
MyBatis 支持使用 ${} 和 #{}两种符号来进行动态SQL的拼接,可能有些人没有留意到,认为 ${} 和 #{} 的作用是差不多的,其实这两者的功能虽然相似,但区别是比较大的!而且,关于这两者的区别,也是Mybatis面试时的一个高频问题,今天百泽老师就带各位一起来看看两者的区别到底有哪些。
三. `#` 和 `$`
1.基本用法
我们先来了解一下 `#`和 `$` 的基本用法。这两个都可以进行动态 SQL 的拼接,但用法上略微有一些差异。
我们先来看 `#` 的用法:
select * from t_user where name = #{param}
如果想执行相同的 SQL,用 $ 写法如下:
select * from t_user where name = '${param}'
眼尖的小伙伴可能已经发现,使用 `$` 比使用 `#` 多了一对单引号,为什么要这样呢?这里就涉及到了两者的第一个区别了:
#
会进行预编译,而且会进行类型匹配,参数是在编译之后填充进去的,所以不需要加上单引号;
$
不会进行数据类型的匹配,只是单纯地进行字符串的拼接,所以要自己手动加上单引号,否则最终拼接出来的SQL语句就不对了。
用过JDBC的小伙伴可能会知道,这就跟JDBC中的Prepare Statement和Statement的区别是一样的,使用 `#` 就相当于使用 PrepareStatement,而使用 `$` 就相当于使用 Statement。
好啦,这就是两者的第一个区别!那么还有其他的区别吗?在讲第二个区别之前,百泽老师先给小伙伴们介绍一下什么是SQL注入。
2.什么是SQL注入
在我们的日常开发中,对于网页上的操作,归根结底无外乎就是增删改查,所以有很多同行都自嘲自己是CURD工程师。
在CURD的过程中,以SQL查询为例,假设我们想根据用户名和密码查询某一个用户是否存在,现在要让用户在前端根据用户名和密码进行查询。
现在假设我们后台的SQL语句如下:
select * from user where username='${username}' and password='${password}'
前端网页有两个输入框,一个是用户名输入框,另一个是密码输入框。假设用户老老实实地输入用户名为zhangsan,密码为123,那么最终生成的SQL语句如下:
select * from user where username='zhangsan' and password='123'
以上过程似乎很完美!
但是!!!要是有人不老实呢?
假设用户输入的用户名是 `zhangsan' or 1=1 #`,密码则随意给了个值 111,那么最终生成的 SQL语句可能就是下面这样了:
select * from user where username='zhangsan' or 1=1 #' and password='123'
大家知道,# 在 SQL语句中的含义是注释,#后面的SQL内容是不执行的。所以这个SQL无论怎么执行,最终都会返回true。这就是所谓的SQL注入了。
如果我们想要防止SQL注入,其实有很多解决办法,其中最直接的一种,就是不要使用$符号来拼接 SQL语句!
3.`#`和`$`的区别
看了上面的例子,现在大家就明白了,相比于 $ ,#有一个重要的作用,那就是可以防止SQL注入!
变量传递时,必须使用#。使用 #{} 就等于使用了JDBC 中 PrepareStatement 这种占位符的形式,既可以提高SQL的执行效率,又可以防止SQL注入等问题。
$ 只是一种简单的字符串拼接,要特别小心 SQL注入的问题。如果我们在项目中使用了$,那么可以自定义过滤器或者通过Druid等工具,来防止SQL注入。
如果在一个场景中,既能使用 # 又能使用 $,那么建议首选 #。
四. 什么情况下只能用$
另外有一个特殊场景只能用$!那就是将数据库对象作为参数传递,其中最常见的就是 group by了。假设我们现在想要对某个表进行排序查询,但参与排序的字段并不确定,而是要通过前端参数传递过来,那么此时就必须使用 $ 了!因为 # 预处理之后,会把数据库中的字段名识别为字符串,进而会出错,如下所示:
select count(*) from user group by ${param}
当然,上面这只是其中的一个例子,实际情况是,只要遇到将数据库的字段名作为参数传递的情况,都得用 $!
五. 小结
最后,百泽老师再来给大家简单总结一下两者的区别:
#
会进行预编译,而且会进行类型匹配,参数是在编译之后填充进去的,所以不需要加上单引号;
$
不会进行数据类型匹配,只是一种单纯的字符串拼接,所以要自己手动加上单引号,否则最终拼接出来的 SQL语句会有问题。不过也要看具体的使用场景,如果参数是列名,就不用加单引号;
`#` 能够预防 SQL 注入问题,$ 不能;
当需要遇到将数据库的字段名作为参数传递时,应该使用用 $。
现在,你知道Mybatis中的#与$有哪些区别了吗?
扫码添加请备注:java