MyBatis中#{}和${}的区别
- #{}和${}都是MyBatis提供的sql参数替换。区别是:
- #{}是预编译处理,${}是字符串直接替换。
- #{}可以防止SQL注入,${}存在SQL注入的风险,例如 “' or 1='1”
- 虽然存在SQL注入风险,但也有自己的适用场景,比如排序功能,表名,字段名等作为参数传入时。
- #{}模糊查询要搭配使用mysql内置的拼接函数concat,安全性高。模糊查询虽然${}可以完成,但是存在SQL注入,不安全。
直接替换是指:是MyBatis 在处理 ${} 时,就是把 ${} 替换成变量的值。
预编译处理是指:MyBatis 在处理#{}时,会将 SQL 中的 #{} 替换为?号,使⽤ PreparedStatement 的 set ⽅法来赋值。
$是直接替换,传入的SQL参数若遇到String类型时,不会自动加单/双引号,就会报错,必须加单/双引号才不出错。并且还有sql注入问题。不过$也有自己的使用场景,比如排序,传入desc,asc字符串时,是不需要加单/双引号的。此时就可以用$,并且这两个字符串不让用户自己传,直接给升序,降序按钮,也就避免了sql注入风险。能发生SQL注入主要还是为用户提供了输入框,用户能传参。
而#无论是Integer类型,还是String类型,都会提前预编译,预编译SQL,而且预编译SQL的性能更高。遇到String类型会自动加单/双引号。
以模糊查询为例,${}和#{}的使用区别
- 使用${},但存在SQL注入风险。
<select id="getBookByNname" resultType="com.example.demo.entity.BookInfo">
select * from book_info where book_name like '${bookName}';
</select>
- 使用#{},更安全,更高效。
<select id="getBookByName" resultType="com.example.demo.entity.BookInfo">
select * from book_info where book_name like concat('%',#{bookName},'%');
</select>
SQL注入问题${}
代码演示,所传参数后跟 ' or '1=1 。就会查出全结果集。
bookMapper.getBookByN("平凡的世界' or '1=1"); //传入参数
select * from book_info where book_name = '${bookName}'; //SQL语句
SQL日志打印:
动态 SQL 语法
1.<if>标签
- 使用场景:当我们在输入个人信息的时候,不一定都得填写(必填项+非必填项),这时候有的参数就为空。所以在插入时就得判空。
<insert id="insert" useGeneratedKeys="true" keyProperty="id">
insert into userinfo {
username,
password,
nickname,
<if test="sex != null"> //test中的sex是属性,不是字段
sex,
</if>
}
birthday
)values (
#{username},
#{password},
#{nickname},
<if test="sex !=null"> //test中的这里的sex是属性
#{sex},
</if>
#{birthday}
)
</insert>
注意 test 中的 sex,是传⼊对象中的属性,不是数据库字段。
2.<trim>标签
prefix:表示整个语句块,以prefix的值作为前缀
suffix:表示整个语句块,以suffix的值作为后缀
prefixOverrides:表示整个语句块要去除掉的前缀
suffixOverrides:表示整个语句块要去除掉的后缀
- 如果输入参数全是非必填项。就需要if标签和trim标签相结合。
<insert id="ss" useGeneratedKeys="true" keyProperty="id">
insert into userinfo
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="username != null">username,</if> //别忘记逗号
<if test="password != null">password,</if> //test中的是属性,不是字段
<if test="nickname != null">nickname,</if>
<if test="sex != null">sex,</if>
<if test="birthday != null">birthday,</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="username != null">#{username},</if>
<if test="nickname != null">#{nickname},</if>
<if test="sex != null">#{sex},</if>
<if test="birthday != null">#{birthday},</if>
</trim>
</insert>
3.<where>标签
- 对于where后跟的参数是否为空,不清楚时。
<select id="select" parameterType="com.example.demo.entity.UserInfo"> //parameterType为
传入的参数类型
select * from usrinfo
<where>
<if test="username != null">and username = #{username}</if>
<if test="userId != null">and userId = #{userId}</if>
<if test="sex != null">and sex = #{sex}</if>
</where>
</select>
以上标签也可以使⽤ <trim prefix="where" suffixOverrides="and"> 替换。
4.<set>标签
- 动态update操作
//parameterType 为传入参数的类型
<update id="update" parameterType="com.example.demo.entity.UserInfo">
update userinfo
<set>
<if test="username != null">username = #{username},</if>
<if test="password != null">password = #{password},</if>
<if test="nickname != null">nickname = #{nickname},</if>
<if test="sex != null">sex = #{sex},</if>
<if test="birthday != null">birthday = #{birthday},</if>
</set>
where = #{id}
</update>
以上标签也可以使⽤ <trim prefix="set suffixOverrides=","> 替换。
5.<foreach>标签
- 遍历集合(如List)时,可以使用,例如批量删除等操作。
// collection 集合类型 item集合名
<delete id="deleteByIds">
delete from userinfo where id in
<foreach collection="list" item="item" open="(" close=")" separator=",">
#{list}
</foreach>
</delete>