//错误
<update id="updateBatch" parameterType="java.util.List"><foreach collection="list" item="item" index="index" separator=";">update manage<set><if test="item.userName != null">userName=#{userName},</if><if test="item.passWord != null">passWord=#{passWord},</if><if test="item.realName != null">realName=#{realName}</if></set>where 1=1<if test="item.id!=null">and id=#{item.id}</if></foreach></update>//正确
<update id="updateBatch" parameterType="java.util.List"><foreach collection="list" item="item" index="index" separator=";">update manage<set><if test="item.userName != null">userName = #{item.userName},</if><if test="item.passWord != null">passWord = #{item.passWord},</if><if test="item.realName != null">realName = #{item.realName}</if></set>where 1=1<if test="item.id != null">and id = #{item.id}</if></foreach></update>
核心结论
这些空格大部分是【必要的】,它们直接影响到最终生成的SQL语句的正确性和可读性。 第二段代码在正确性上更优,主要是因为它修正了第一段代码中一个严重的错误。
一、逐行对比分析
让我们把两段代码的关键不同点拆解出来:
1. 赋值语句右边的空格(关键错误修正)
-
第一段代码(错误):
userName=#{userName},
-
问题:
#{userName}
前面没有空格。这会导致生成的SQL语句变成userName=John,
,这是一个严重的语法错误。SQL要求赋值操作符两边必须有空格(虽然不是所有数据库都强制要求,但这是标准SQL约定,没有空格非常容易出错)。
-
-
第二段代码(正确):
userName = #{item.userName},
-
修正:
=
两边都有空格,生成的SQL是userName = 'John',
,这是完全符合SQL语法的标准写法。
-
2. 参数引用的完整性(关键错误修正)
-
第一段代码(错误):
#{userName}
-
问题: 它缺少了
item.
前缀。在 MyBatis 的<foreach>
循环中,每个元素用item="item"
指定了别名。要访问当前遍历对象的属性,必须使用item.属性名
。这里写#{userName}
,MyBatis 会去查找一个叫userName
的顶级参数,而不是你传入的List中某个对象的属性。这会导致参数绑定失败,值变为null
。
-
-
第二段代码(正确):
#{item.userName}
-
修正: 正确地引用了当前循环对象
item
的userName
属性。
-
3. WHERE
和 AND
前的空格(必要且重要)
-
第一段代码(有风险):
where 1=1 <if...>and id=#{item.id}</if>
-
分析:
where 1=1
和后面的and id...
拼接时,会生成where 1=1and id=1
。虽然1=1and
在大多数数据库中被解析为1=1 and
(因为and
是关键字),但这种依赖数据库自动识别的做法是有风险的,非常不规范。
-
-
第二段代码(最佳实践):
where 1=1 <if...> and id = #{item.id}</if>
-
修正: 在
and
前面显式地加上了一个空格。这样就能确保生成标准的where 1=1 and id = 1
。这是非常良好且必要的习惯。
-
4. 其他空格(主要为了可读性)
像 item.id != null
等表达式中的空格,以及标签内的一些换行和缩进,主要是为了让XML代码本身更清晰、更易读,对最终生成的SQL没有影响。MyBatis在解析动态SQL标签(<if>
, <set>
等)时,会智能地处理掉多余的空白字符,只保留SQL语句本身必要的空格。
二、最终生成的SQL对比
假设我们传入的List包含一个对象:{id: 1, userName: "Admin"}
-
第一段错误代码生成的SQL:
update manage set userName=, passWord=?, realName=? where 1=1and id=?;
这是一条完全错误的SQL! 原因:
-
userName=
后面没有空格,语法怪异。 -
#{userName}
因引用错误导致值为null
,所以userName=
后面直接是逗号,语法错误。 -
where 1=1and id=?
缺少空格,不规范。
-
-
第二段正确代码生成的SQL:
update manage set userName = 'Admin', where 1=1 and id = 1;
这是一条语法正确、清晰明了的SQL。(注意:即使多了一个逗号,
<set>
标签也会负责去掉它)。
三、总结
差异点 | 第一段代码 | 第二段代码 | 重要性 |
---|---|---|---|
赋值空格 (= ) |
userName=#{...} |
userName = #{...} |
【关键】:缺少空格会导致SQL语法错误或不规范。 |
参数引用 | #{userName} |
#{item.userName} |
【致命】:缺少 item. 会导致参数绑定失败,值为null。 |
条件空格 (and ) |
and id=#{...} |
and id = #{...} |
【重要】:显式的空格是良好实践,保证SQL100%正确。 |
代码格式 | 空格较少,紧凑 | 空格较多,清晰 | 【一般】:影响开发者阅读代码的体验,不影响SQL生成。 |
结论:第二段代码不仅仅是“多了些空格”,而是修正了第一段代码中存在的两个严重错误(参数引用和SQL语法),是正确、可靠且符合规范的写法。 在编写MyBatis动态SQL时,保证SQL语句本身格式的正确性至关重要。