MyBatis的xml中字符串类型判空与非字符串类型判空处理方式
orderNo != null
这种非字符串类型的判断,也能用 Java 静态方法来替代,比如用 Hutool 的 ObjectUtil.isNotNull()
或 ObjectUtil.isNotEmpty()
。
我帮你把 字符串 用 StrUtil.isNotBlank()
,非字符串 用 ObjectUtil.isNotNull()
的完整 MyBatis updateById
版本写好,这样判断逻辑会很统一。
完整 Hutool 写法版本
<update id="updateById" parameterType="com.example.NursingProject">UPDATE nursing_project<set><if test="@cn.hutool.core.util.StrUtil@isNotBlank(name)">name = #{name},</if><if test="@cn.hutool.core.util.ObjectUtil@isNotNull(orderNo)">order_no = #{orderNo},</if><if test="@cn.hutool.core.util.StrUtil@isNotBlank(unit)">unit = #{unit},</if><if test="@cn.hutool.core.util.ObjectUtil@isNotNull(price)">price = #{price},</if><if test="@cn.hutool.core.util.StrUtil@isNotBlank(image)">image = #{image},</if><if test="@cn.hutool.core.util.StrUtil@isNotBlank(nursingRequirement)">nursing_requirement = #{nursingRequirement},</if><if test="@cn.hutool.core.util.ObjectUtil@isNotNull(status)">status = #{status},</if><if test="@cn.hutool.core.util.StrUtil@isNotBlank(createBy)">create_by = #{createBy},</if><if test="@cn.hutool.core.util.StrUtil@isNotBlank(updateBy)">update_by = #{updateBy},</if><if test="@cn.hutool.core.util.StrUtil@isNotBlank(remark)">remark = #{remark},</if><if test="@cn.hutool.core.util.ObjectUtil@isNotNull(updateTime)">update_time = #{updateTime},</if></set>WHERE id = #{id}
</update>
对比优化
-
原本 MyBatis 写法
<if test="orderNo != null">...</if>
需要区分字符串、数字、对象分别写不同条件。
-
现在 Hutool 写法
- 字符串 →
StrUtil.isNotBlank()
(避免空格也被当作有效值) - 数字/对象 →
ObjectUtil.isNotNull()
(仅判断 null,不管 0/false)
- 字符串 →
这样代码风格统一,可读性也更好。
这个现象其实是 MyBatis + OGNL 的自动类型推断 惹的祸。
你这里:
<if test="@cn.hutool.core.util.ObjectUtil@isNotNull(price)">price = #{price},
</if><if test="@cn.hutool.core.util.ObjectUtil@isNotNull(status)">status = #{status},
</if>
表面上看用的都是同一个 ObjectUtil.isNotNull()
,但实际上 price 和 status 的运行时类型不一样:
字段 | Java 类型 | JSON 传值类型 | MyBatis 解析时的实际类型 |
---|---|---|---|
price | BigDecimal | 50 | java.math.BigDecimal |
status | Integer | 1 | java.lang.Long |
为什么 status
变成 Long
?
MyBatis 在 JSON 反序列化(或者参数绑定)时,如果是整数值但没有显式指定类型,很多情况下会用 Long
表示(尤其是 Map 或动态 SQL 场景中)。
ObjectUtil.isNotNull(1L)
本身没问题,但在 OGNL 调用静态方法时,有些版本的 MyBatis/OGNL 会先尝试做类型转换(尤其是如果方法签名有重载或匹配不明确时),于是会触发 "Unable to convert type java.lang.Long to java.lang.CharSequence"
这种异常。
为什么 price 没事?
BigDecimal
类型在 OGNL 里不会被误判去做字符串转换,因为它本来就不是 CharSequence
,且 ObjectUtil.isNotNull()
的签名 isNotNull(Object)
匹配得很干脆,没有触发额外的类型推断。
怎么解决?
最稳妥的方式就是 不要用静态方法去判断 null,直接用 OGNL 原生的 null 判断:
<if test="price != null">price = #{price},
</if><if test="status != null">status = #{status},
</if>
这样不涉及静态方法匹配,就不会出现 Long
转 CharSequence
的问题。
如果你一定要用 ObjectUtil.isNotNull
:
<if test="@cn.hutool.core.util.ObjectUtil@isNotNull((Object)status)">status = #{status},
</if>
加 (Object)
是为了让 OGNL 不去尝试把 Long
当作别的类型匹配。
如果单纯比较性能,原生 != null
判断 会明显比 StrUtil.isNotBlank()
快。
原因很简单:
判断方式 | 逻辑 | 性能分析 |
---|---|---|
!= null | 直接用 OGNL 判断对象是否为 null,属于一次简单的引用比较 | O(1) 操作,几乎没有额外开销 |
StrUtil.isNotBlank() | Hutool 工具方法,需要静态方法调用 + 类型检查 + 遍历字符串判断空格 | 至少多了方法调用开销 + 字符遍历(字符串越长耗时越多) |
举个例子(粗略对比执行步骤):
1. != null
- MyBatis OGNL:取值
- JVM:引用地址是否为
null
→ true/false - 总耗时 ≈ 一次内存地址比较
2. StrUtil.isNotBlank()
-
MyBatis OGNL:加载
StrUtil
类 -
JVM:调用静态方法
-
方法内部:
- 判断对象是否为
null
- 调用
String.length()
- 遍历每个字符判断是否为空格字符
- 判断对象是否为
-
总耗时 ≈ 多次方法调用 + 字符遍历
结论
- 非字符串类型 →
!= null
是最优方案(少步骤、无额外逻辑) - 字符串类型 → 如果需要判断空字符串和全空格情况,那必须用
isNotBlank()
(功能多,但慢一点) - 如果字符串允许空格或空值存储,不做空格判断,用
!= null
会快很多
建议:
- 数值、日期、枚举 → 全部用
!= null
- 字符串 → 如果必须去掉空格判断为空,才用
isNotBlank()
;否则也用!= null