视界信息网
Article

MyBatis `if` 标签:别再掉进 List 非空判断的坑里!

发布时间:2026-02-03 17:42:02 阅读量:2

.article-container { font-family: "Microsoft YaHei", sans-serif; line-height: 1.6; color: #333; max-width: 800px; margin: 0 auto; }
.article-container h1

MyBatis `` 标签:别再掉进 List 非空判断的坑里!

摘要:MyBatis 的 `` 标签在动态 SQL 中扮演着重要角色,但 List 非空判断却暗藏玄机。本文以一个线上 Bug 为切入点,深入剖析 `list != null` 的潜在问题,并结合不同业务场景,提供更严谨的“最佳实践”。同时,探讨自定义 OGNL 方法扩展 MyBatis 的可能性,以及代码审查 Checklist,助你写出更健壮的 MyBatis XML 配置文件。

引言:线上 Bug 的血泪教训

话说 2026 年初,我接到一个紧急告警:线上订单服务突然出现大面积查询超时。经过一番排查,罪魁祸首竟然是一个看似人畜无害的 MyBatis XML 配置文件。

年轻的王二,为了赶进度,直接从 Stack Overflow 上抄了一段 <if test="list != null"> 的代码,用于判断订单 ID 列表是否为空。这段代码在本地测试环境跑得飞起,王二也就没多想,直接提交了。结果上线后,当传入一个空的订单 ID 列表时,SQL 并没有像预期的那样跳过 WHERE id IN (...) 这个条件,而是仍然执行了,导致数据库压力剧增,最终引发了雪崩。

更让人头疼的是,监控日志里根本看不出任何异常,因为 SQL 语句本身是合法的。这就像一个隐藏的定时炸弹,直到爆炸的那一刻,你才意识到问题的严重性。这种 Bug 的排查难度,啧啧,谁经历过谁知道。

深层原因分析:OGNL 的“障眼法”

为什么 list != null 在 MyBatis 中会失效?这得从 MyBatis 使用的 OGNL 表达式说起。 OGNL 表达式在判断 list != null 时,并非总是像 Java 代码那样直接判断 List 对象是否为 null。

在 MyBatis 的上下文中,OGNL 表达式的求值会受到很多因素的影响,比如:

  • 参数传递:即使 List 本身是空的,但如果 MyBatis 上下文中还传递了其他参数,OGNL 可能会错误地认为整个上下文“不为空”,从而导致 list != null 返回 true
  • 类型转换:MyBatis 会进行一些类型转换,这可能会导致一些意想不到的结果。例如,如果 List 的类型不匹配,MyBatis 可能会尝试进行类型转换,这可能会导致 list != null 的结果不符合预期。

说白了,MyBatis 里的 null 和 Java 里的 null,有时候不是一回事。这就像魔术师的障眼法,让你觉得你看到的就是真相,但实际上你已经被耍了。

“最佳实践”辨析:没有万能公式

别指望我给你一个“万能公式”,告诉你 List 非空判断应该怎么写。软件工程这行,哪有什么“银弹”?一切都要具体问题具体分析。

  • 场景一:允许 List 为 null,也允许执行后续逻辑

    这种情况下,简单的 list != null 可能就足够了。例如,你的 SQL 只是根据 List 的值来添加一些额外的过滤条件,即使 List 为 null,也允许执行一个不带任何条件的查询。

    xml <select id="selectOrders" resultType="Order"> SELECT * FROM orders <where> <if test="list != null"> AND order_id IN <foreach item="item" collection="list" open="(" separator="," close=")"> #{item} </foreach> </if> </where> </select>

  • 场景二:List 必须非空才能执行特定 SQL 片段

    这种情况下,list != null and !list.isEmpty() 才是更严谨的选择。isEmpty() 方法相对于 size() > 0 在语义上更清晰地表达了“空”的概念,也更符合代码的可读性。

    xml <select id="selectOrders" resultType="Order"> SELECT * FROM orders <where> <if test="list != null and !list.isEmpty()"> AND order_id IN <foreach item="item" collection="list" open="(" separator="," close=")"> #{item} </foreach> </if> </where> </select>

  • 场景三:借助第三方库

    可以使用 Apache Commons Collections 提供的 CollectionUtils.isNotEmpty(list) 方法。优点是代码更简洁,缺点是引入了额外的依赖。 是否值得引入,取决于你的项目情况。

    xml <select id="selectOrders" resultType="Order"> SELECT * FROM orders <where> <if test="@org.apache.commons.collections4.CollectionUtils@isNotEmpty(list)"> AND order_id IN <foreach item="item" collection="list" open="(" separator="," close=")"> #{item} </foreach> </if> </where> </select>

    注意: 使用第三方库需要在 MyBatis 的配置文件中进行相应的配置,具体可以参考 MyBatis 的官方文档。

方法 优点 缺点 适用场景 潜在风险
list != null 简单 可能存在 OGNL 表达式陷阱 允许 List 为 null,也允许执行后续逻辑 当 List 为空时,可能导致 SQL 语句执行不符合预期
list != null and !list.isEmpty() 严谨,语义清晰 略显冗长 List 必须非空才能执行特定 SQL 片段
CollectionUtils.isNotEmpty(list) 代码简洁 引入额外依赖 代码简洁性优先,且项目已经引入了 Apache Commons Collections 需要确保项目已经引入了 Apache Commons Collections,否则会报错

高级技巧:自定义 OGNL 方法

如果你对 MyBatis 的扩展性有更高的要求,可以考虑自定义 OGNL 方法。例如,可以自定义一个名为 isNotEmptyList 的方法,专门用于判断 List 是否非空。

  1. 编写自定义 OGNL 方法:

    java public class OgnlUtils { public static boolean isNotEmptyList(List<?> list) { return list != null && !list.isEmpty(); } }

  2. 配置 MyBatis 使用自定义方法:

    在 MyBatis 的配置文件中,添加如下配置:

    xml <configuration> <typeAliases> <typeAlias alias="OgnlUtils" type="com.example.OgnlUtils"/> </typeAliases> </configuration>

  3. 在 XML 配置文件中使用自定义方法:

    xml <select id="selectOrders" resultType="Order"> SELECT * FROM orders <where> <if test="@OgnlUtils@isNotEmptyList(list)"> AND order_id IN <foreach item="item" collection="list" open="(" separator="," close=")"> #{item} </foreach> </if> </where> </select>

这种方法的优点是可以将 List 非空判断的逻辑封装在一个地方,方便维护和复用。缺点是增加了代码的复杂度,需要对 MyBatis 的扩展机制有一定的了解。

代码审查 Checklist:防患于未然

为了避免再次发生类似王二的悲剧,我总结了一份代码审查 Checklist,希望能帮助大家在提交代码之前自查 MyBatis XML 配置文件中的 List 非空判断是否存在潜在问题:

  • 是否考虑了 List 为 null 的情况?
  • 是否使用了正确的 OGNL 表达式?
  • 是否与业务逻辑一致?
  • 是否经过了充分的测试?
  • 是否考虑了 MyBatis 的类型转换可能带来的影响?

总结:魔鬼在细节中

List 非空判断看似简单,实则暗藏玄机。别轻信“万能公式”,多思考,多测试,才能避免掉进坑里。记住,魔鬼 往往藏在细节之中。

小心驶得万年船,希望这篇文章能帮助你写出更健壮的 MyBatis XML 配置文件,远离线上 Bug 的困扰!

参考来源: