Mybatis从SQL注入到OGNL注入

动态SQL

动态 SQL 是 MyBatis 的强大特性之一,一般而言,如果不使用动态SQL来拼接SQL语句,是比较痛苦的,比如拼接时要确保不能漏空格,还要注意去掉列表最后一个列名的逗号等,但是利用动态 SQL,就可以彻底摆脱这种痛苦。

一般而言,使用mybatis有两种配置,一种是通过xml文件的方式来配置,另一种是通过注解的方式来配置。

1、xml文件

mybatis的*mapper.xml文件里能够使用动态SQL的标签有4种,分别是:

① if

if标签是Mybatis中使用动态SQL比较频繁的地方,尤其是在where的判断里,比如:

1
2
3
4
5
6
<select id="findActiveBlogWithTitleLike" resultType="Blog">
SELECT * FROM BLOG WHERE state = 'ACTIVE'
<if test="title != null">
AND title like #{title}
</if>
</select>

这里的SQL语句就提供了选择情景,如果我们不传入title或者传入的title为空,那么就不会拼接 AND title like #{title}

又或者想加入额外的判断:

1
2
3
4
5
6
7
8
9
10
<select id="findActiveBlogLike"
resultType="Blog">
SELECT * FROM BLOG WHERE state = 'ACTIVE'
<if test="title != null">
AND title like #{title}
</if>
<if test="author != null and author.name != null">
AND author_name like #{author.name}
</if>
</select>

结论:if标签里的 test属性,可以插入并解析OGNL表达式

② choose (when, otherwise)

根据官方文档中的说明

有时候,我们不想使用所有的条件,而只是想从多个条件中选择一个使用。针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<select id="findActiveBlogLike" resultType="Blog">
SELECT * FROM BLOG WHERE state = 'ACTIVE'
<choose>
<when test="title != null">
AND title like #{title}
</when>
<when test="author != null and author.name != null">
AND author_name like #{author.name}
</when>
<otherwise>
AND featured = 1
</otherwise>
</choose>
</select>

结论:when标签里的 test属性,可以插入并解析OGNL表达式

③ trim (where, set)

1
2
3
4
5
6
7
8
9
10
11
12
<select id="findActiveBlogLike"  resultType="Blog">
SELECT * FROM BLOG WHERE
<if test="state != null">
state = #{state}
</if>
<if test="title != null">
AND title like #{title}
</if>
<if test="author != null and author.name != null">
AND author_name like #{author.name}
</if>
</select>

可以看下这个SQL语句,假设如果没有满足匹配的条件,那么最终这条 SQL 会变成这样:

1
2
SELECT * FROM BLOG
WHERE

毫无疑问,这会导致查询失败

同样的,如果匹配的只是第二个条件,这条 SQL 会是这样:

1
2
3
SELECT * FROM BLOG
WHERE
AND title like 'someTitle'

这个查询也会失败

所以mybatis提出来了trim方法,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<select id="findActiveBlogLike" resultType="Blog">
SELECT * FROM BLOG
<where>
<if test="state != null">
state = #{state}
</if>
<if test="title != null">
AND title like #{title}
</if>
<if test="author != null and author.name != null">
AND author_name like #{author.name}
</if>
</where>
</select>

可以看到多了一个where标签,同理还有一个set标签

结论:该情况下,一般没有地方可以供我们插入OGNL表达式

④ foreach

动态 SQL 的另一个常见使用场景是对集合进行遍历(尤其是在构建 IN 条件语句的时候)。比如:

1
2
3
4
5
6
7
8
9
10
<select id="selectPostIn" resultType="domain.blog.Post">
SELECT *
FROM POST P
<where>
<foreach item="item" index="index" collection="list"
open="ID in (" separator="," close=")" nullable="true">
#{item}
</foreach>
</where>
</select>

结论:该情况下,一般没有地方可以供我们插入OGNL表达式

⑤ bind

bind 标签允许我们在 OGNL 表达式以外创建一个变量,并将其绑定到当前的上下文。比如:

1
2
3
4
5
<select id="selectBlogsLike" resultType="Blog">
<bind name="pattern" value="'%' + _parameter.getTitle() + '%'" />
SELECT * FROM BLOG
WHERE title LIKE #{pattern}
</select>

结论:bind标签里的 value属性,可以插入并解析OGNL表达式

2、注解

springboot使我们摆脱了各种xml配置的烦恼,对应的,mybatis也为springboot提供了对应的注解来满足动态SQL的功能,主要有以下注解:

  • @Insert
  • @Update
  • @Delete
  • @Select
  • @InsertProvider
  • @SelectProvider
  • @UpdateProvider
  • @DeleteProvider

@Insert、@Update、@Delete和@Select这四个注解对应的是数据库增删改查功能,每一个都有一个对应的Provider注解标识

带有Provider注解和不带有Provider注解的区别是,使用Provider需要自己实现查询类,并且使用动态SQL也简单很多。

举个例子,如果@Update注解想要实现动态SQL,那么一定要使用


Mybatis从SQL注入到OGNL注入
http://www.qetx.top/posts/30828/
作者
Qetx.Jul.27
发布于
2023年11月2日
许可协议