<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>
<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>
<select id="findTeacherByName" resultMap="BaseResultMap" parameterType="com.example.mybatis.entity.Teacher"> select id,email from Teacher where name = ${name}; </select>
public String countUserByRolePM(final UserVO userVO) { StringBuffer sb = new StringBuffer(); sb.append("SELECT count(*) FROM ( "); sb.append(" SELECT A.*,count(P.Id) FROM ("); sb.append(" SELECT U.id,U.name,DD.referrer,U.mobilePhone ,U.country ,U.city,U.goodAtIndustry,U.englishAbility,U.goodAtArea,U.state,U.createTime,U.modifyTime FROM T_USER U LEFT JOIN T_USER_ROLE UR ON U.id = UR.userId " + " LEFT JOIN (SELECT A.id,B.name as referrer FROM T_USER AS A INNER JOIN T_USER as B ON A.referrer = B.id) as DD ON DD.id = U.id WHERE 1=1 ");
if (roleids != null){ for (int i = 0 ; i< roleids.length ; i ++){ String s = roleids[i]; if(i != roleids.length -1){ sb.append("'" + s + "'" + ","); }else{ sb.append("'" + s + "'"); } } } sb.append(")");
if(!StringUtils.isEmpty(userVO.getName())){ sb.append(" AND U.name LIKE CONCAT('%',#{name},'%')"); } if(!StringUtils.isEmpty(userVO.getMobilePhone())){ sb.append(" AND U.mobilePhone = #{mobilePhone}"); } if(!StringUtils.isEmpty(userVO.getCity())){ sb.append(" AND U.city LIKE CONCAT('%',#{city},'%')"); } if(!StringUtils.isEmpty(userVO.getRegion())){ sb.append(" AND U.region LIKE CONCAT('%',#{region},'%')"); } if(!StringUtils.isEmpty(userVO.getPlatformLevel())){ sb.append(" and U.platformLevel = #{platformLevel}"); } if(!StringUtils.isEmpty(userVO.getGoodAtIndustry())){ sb.append(" and find_in_set(#{goodAtIndustry},U.goodAtIndustry)"); } if(!StringUtils.isEmpty(userVO.getState())){ sb.append(" and U.state = #{state}"); } sb.append(" GROUP BY U.id"); sb.append(" ) A"); sb.append(" LEFT JOIN T_PROJECT P ON P.pmId = A.id"); sb.append(" GROUP BY A.id"); sb.append(" ORDER BY A.modifyTime DESC"); sb.append(") as A"); return sb.toString(); }
这样形成的SQL语句,实际上就是相当于生成了一个XML文件:
1 2 3
<select id="findTeacherByName" resultMap="BaseResultMap" parameterType="com.example.mybatis.entity.Teacher"> select id,email from Teacher where name = 传入的name值 </select>
那这样的方式和bind标签里的 value 属性或者**${param} 参数中**有没有区别呢?
当然有区别,并且这种区别是本质的
正是前文中提到的:解析顺序
这种形成的SQL语句会首先进行OGNL表达式,然后再执行查询。
以下面的Provider为例:
1 2 3 4 5 6 7 8 9 10 11 12 13
public String findTeacherByName(Map<String, Object> map) { String name = (String) map.get("name"); String s = new SQL() { { SELECT("id,email"); FROM("Teacher"); if(map.get("id")!=null) WHERE("name=" + name); } }.toString(); return s; } }
如果我们传入name的值为:**${@java.lang.Math@min(4,10)}**
其流程是这样的:
首先生成了SQL语句为:
1
select id,email from Teacher where name = ${@java.lang.Math@min(4,10)};
经过一系列的传递,相当于生成(实际上并未生成,直接解析的)了一个如下的XML文件:
1 2 3
<select id="findTeacherByName" resultMap="BaseResultMap" parameterType="com.example.mybatis.entity.Teacher"> select id,email from Teacher where name = ${@java.lang.Math@min(4,10)}; </select>
然后进行OGNL表达式解析:
1 2 3
<select id="findTeacherByName" resultMap="BaseResultMap" parameterType="com.example.mybatis.entity.Teacher"> select id,email from Teacher where name = '4'; </select>