情景分析
工作当中,动态sql经常会用到,当遇到多个查询条件时,我们可能会写出类似下面这样的sql
<select id="selectUserByUsernameAndSex" resultType="user" parameterType="com.ztt.User">
select *
from user
where
<if test="username != null">
username=#{username}
</if>
<if test="username != null">
and sex=#{sex}
</if>
</select>
这样写会产生很大问题,
如果没有匹配的条件,最终这条 SQL 会变成这样:select * from user where
如果username为空,最终这条 SQL 会变成这样:select * from user where and sex=#{sex}
这两种情况都会产生错误的sql语句。
解决方案
针对这种情况,有人想出了一个办法,就是人工的加一个结果必定为true的条件,比如where 1=1。那么我们的sql就会变成下面这样:
<select id="selectUserByUsernameAndSex" resultType="user" parameterType="com.ztt.User">
select *
from user
where
1 = 1
<if test="username != null">
and username=#{username}
</if>
<if test="username != null">
and sex=#{sex}
</if>
</select>
这样,不管if里面的判断如何,都能保证最终的sql语句是正确的。
但是这样很可能会造成非常大的性能损失,因为添加了 “where 1=1 ”的过滤条件之后,数据库系统就无法使用索引等查询优化策略,数据库系统将会被迫对每行数据进行扫描(即全表扫描) 来比较此行是否满足过滤条件,当表中的数据量较大时查询速度会非常慢;此外,还会存在SQL 注入的风险。
(实际上where 1=1 也会走索引,不影响查问效率,咱们写的sql指令会被mysql 进行解析优化成本人的解决指令,在这个过程中 1 = 1 这类无意义的条件将会被优化。)
那么有什么更好的替换方案呢?答案是使用使用<where>标签。
where标签只会在它包含的标签中有返回值的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where标签也会将它们去除。
使用where标签后,我们的sql就变成了下面这样:
<select id="selectUserByUsernameAndSex" resultType="user" parameterType="com.ztt.User">
select * from user
<where>
<if test="username != null">
username=#{username}
</if>
<if test="username != null">
and sex=#{sex}
</if>
</where>
</select>