利用mybatis标签替换硬编码

建议

利用mybatis标签替换硬编码

背景

在项目中偶尔看到这样的代码:

1
2
3
4
5
6
7
8
9
<sql id="querySqlString">
<where>
1=1
<if test="fundsOrderIdList != null and fundsOrderIdList.size()>0">
and funds_order_id IN
<foreach collection="fundsOrderIdList" item="id" index="index" open="(" close=")" separator=",">
#{id}
</foreach>
</if>

这段代码中有个1=1很扎眼,这个不是bug,也没有什么性能问题,只是程序员世代传承下来的一个习惯。

故事

在很久以前,充满智慧的程序员为了解决动态条件拼接的问题,发明了1=1这个写法,对应的还有1=0的写法,我们来看看这个写法在以前是怎么解决问题的:

1
2
3
4
5
6
7
sql = "select * from car_table where 1=1"
for(Condtion condition:conditions){
/**假如没有1=1,第一个条件直接拼接上and语法就错误了
*还得做出额外判断才行
*/
sql = sql + " and " + condition.field + " = " + condition.value
}

确实很巧妙,而且也没有性能问题,不信我给你举个🌰:

1
select * from pay_channel_with_bank where 1=1 and channel_code = '50008';

这是一条很简单的SQL,其中就有1=1的写法,我们来看看MySQL查询优化器优化后实际执行的SQL:

1
2
3
4
5
EXPLAIN
select * from pay_channel_with_bank where 1=1 and channel_code = '50008';
SHOW WARNINGS;
实际执行的SQL:
/* select#1 */ select 此处省略很多column from `online_paychannel`.`pay_channel_with_bank` where (`online_paychannel`.`pay_channel_with_bank`.`channel_code` = 50008)

可以看到1=1已经被优化掉了。

结论

虽然这种写法没啥大问题,但是代码是给人看的。如果不了解这种写法,乍一眼看过去肯定有点懵。

而且我们现在使用的Mybatis提供的<where>标签本身就会帮我们做优化:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
private void applyPrefix(StringBuilder sql, String trimmedUppercaseSql) {
if (!prefixApplied) {
prefixApplied = true;
if (prefixesToOverride != null) {
for (String toRemove : prefixesToOverride) {
//此处会移除一些前缀
if (trimmedUppercaseSql.startsWith(toRemove)) {
sql.delete(0, toRemove.trim().length());
break;
}
}
}
if (prefix != null) {
sql.insert(0, " ");
sql.insert(0, prefix);
}
}
}

WhereSqlNode中定义的prefixesToOverride:

1
private static List<String> prefixList = Arrays.asList("AND ","OR ","AND\n", "OR\n", "AND\r", "OR\r", "AND\t", "OR\t");

所以使用了<where>标签后可以放心大胆的去掉1=1

作者

太阳当空赵先生

发布于

2020-08-11

更新于

2022-02-22

许可协议

评论