找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

836

积分

0

好友

104

主题
发表于 3 小时前 | 查看: 5| 回复: 0

作为一名 Java 开发者,你一定经历过被 MyBatis XML 支配的恐惧。当你打开一个 UserMapper.xml,迎面而来的是几百行甚至上千行的 <if>, <where>, <choose>, <foreach> 标签。原本清爽的 SQL 语句被这些 XML 标签切割得支离破碎,维护起来如同面对一份冗长的清单,效率低下。

如果你也受够了在 XML 标签里写逻辑,受够了为了一个简单的非空判断就要写三行 XML,那么 dbVisitor 的动态 SQL 规则机制,或许能为你带来全新的开发体验。它旨在让 SQL 回归其简洁的本质。

MyBatis 的 XML 困境

我们先来回顾一个典型的、带有多个查询条件的 MyBatis SQL 是什么样子的:

<select id="queryUsers" resultType="User">
    SELECT * FROM tb_user
    <where>
        <if test="name != null">
            AND name = #{name}
        </if>
        <if test="age != null">
            AND age = #{age}
        </if>
        <if test="status != null">
            AND status = #{status}
        </if>
        <if test="createTime != null">
            AND create_time >= #{createTime}
        </if>
    </where>
</select>

这段代码的问题显而易见:XML 标签与 SQL 语句深度耦合,视觉干扰严重,可读性差。随着查询条件增多,文件会迅速膨胀,维护成本激增。

更优雅的解决方案:dbVisitor 规则引擎

dbVisitor 作为一个新一代的数据库访问工具,其核心设计理念之一就是:让 SQL 回归 SQL

利用其独创的规则机制,上面的 XML 代码可以被极大地精简:

SELECT * FROM tb_user
   @{and, name = :name}
   @{and, age = :age}
   @{and, status = :status}
   @{and, create_time >= :createTime}

假设只传入参数 name="Tom",dbVisitor 引擎将智能地生成如下 SQL:

SELECT * FROM tb_user WHERE name = ?

是不是瞬间清爽了?没有了尖括号的视觉干扰,也没有了冗余的 XML 闭合标签,只剩下清晰的 SQL 逻辑核心。

你可能会好奇:@{and, ...} 做了什么?它仅仅是字符串拼接吗?

当然不是!dbVisitor 的规则是智能的。以 @{and, name = :name} 为例,它内置了以下逻辑:

  • 智能判空:引擎会自动检查表达式中的参数 :name。如果为 null,整个 @{and} 规则块会被自动忽略,不会生成任何 SQL。(注:空字符串 "" 被视为有效值,不会被忽略)。
  • 智能补全:如果内容不为空,@{and} 会识别它是否是 WHERE 子句的开头。如果是(例如前面没有其他条件),它会自动抹去 AND,直接生成 WHERE name = ?

这一切都是自动发生的,开发者只需声明规则,剩下的交给 dbVisitor 处理。

条件判断本该如此简洁

在 MyBatis 中,大部分 <if> 标签都在做两件事:参数非空时追加条件,或根据布尔开关追加条件。dbVisitor 将这两类高频场景内化为最基础的规则。

1. 智能判空:@{and} 与 @{or}

这是最常用的判空规则,它们会自动检查参数是否为 null

SELECT * FROM tb_user
  @{and, name = :name} -- 仅当 name 不为空时生成 and name = ?
  @{or,  age > :age}   -- 仅当 age 不为空时生成 or age > ?

2. 开关控制:@{ifand} 与 @{ifor}

当你需要用布尔值来控制 SQL 片段的生成时,就会用到它们。

-- 只有当 showAll 为 false 时,才拼接入 AND is_delete = 0
SELECT * FROM tb_user @{ifand, !showAll, is_delete = 0}

对比一下 MyBatis 中为了实现相同功能,常常需要写的冗余代码:

SELECT * FROM tb_user WHERE 1=1
<if test="!showAll">
    AND is_delete = 0
</if>

仅仅一个简单的条件,dbVisitor 就让你少写了至少3行代码,并且避免了 1=1 这种妥协式的写法。

一行代码搞定 IN 查询

MyBatis 的 <foreach> 标签配置项繁多,为了处理集合为空的情况,往往还需要额外包裹一层 <if> 标签,繁琐且易错。

SELECT * FROM tb_user WHERE 1=1
<!-- 先判断不为空,再循环 -->
<if test="idList != null and idList.size() > 0">
    AND id IN
    <foreach collection="idList" item="id"
             open="(" separator="," close=")">
      #{id}
    </foreach>
</if>

而在 dbVisitor 中,利用 @{ifand}@{in} 两个规则的嵌套使用,你只需要一行:

-- ifand 接受表达式,!idList.isEmpty() 确保集合有值时才生成 SQL
SELECT * FROM tb_user
  @{ifand, !idList.isEmpty(), id IN @{in, :idList}}

代码量的对比堪称降维打击,逻辑却一目了然。

告别 Update 语句中的逗号烦恼

在编写 Update 语句时,处理动态字段末尾的逗号是最烦人的细节之一。dbVisitor 的 @{set} 规则完美解决了这个问题,引擎会自动处理字段间的逗号分隔。

UPDATE tb_user
SET
    @{set, name  = :name}
    @{set, age   = :age}
    @{set, email = :email}
WHERE id = #{id}

而 MyBatis 需要用 <set><if> 标签组合来应对:

UPDATE tb_user
<set>
    <if test="name != null">name = #{name},</if>
    <if test="age != null">age = #{age},</if>
    <if test="email != null">email = #{email},</if>
</set>
WHERE id = #{id}

需要注意的是,@{set} 规则的判断依赖于已生成的 SQL 上下文。最佳的实践是先写固定列,再写动态列,让规则能正确推断和处理前置逗号。

-- 正确的写法
UPDATE tb_user SET
    fixed_col = 123        -- 先写固定列
    @{set, name = :name}   -- 规则会自动处理前置逗号
    @{set, email = :email}
WHERE id = :id

分支判断:像编程一样写 SQL

MyBatis 的 <choose>-<when>-<otherwise> 结构冗长,即便在 @Select 注解中,也逃不脱嵌入 <script> 标签的命运。

dbVisitor 让你用更符合编程直觉的 SQL 思维来处理分支,它支持两种模式:

IF-ELSE 模式

SELECT * FROM t_blog
@{and, @{case, ,
          @{when, title != null,   title = #{title}},
          @{when, content != null, content = #{content}},
          @{else,                  owner = "defaultOwner"}
        }
}

Switch 模式

-- 根据 encryptMode 的值选择加密方式
SELECT * FROM tb_user
@{and, @{case, encryptMode,
          @{when, true, password = @{md5, :pwd}},
          @{else,       password = :pwd}
        }
}

强大的规则组合:像搭积木一样灵活

规则引擎最强大的特性在于其可组合性。规则可以像乐高积木一样嵌套使用,这意味着你可以用 @{case} 的结果去驱动 @{and},或者在 @{else} 里嵌入另一组条件。

例如,实现一个常见的权限控制场景:

  • 管理员 (ADMIN) 查询所有数据;
  • 部门经理 (MGR) 查询本部门数据;
  • 普通员工只能查自己的数据。
SELECT * FROM tb_report
@{and, @{case, role, @{when, 'ADMIN', /* 不加限制 */},
                     @{when, 'MGR',   dept_id = :deptId},
                     @{else,          user_id = :userId}
        }
}

注意外层的 @{and} 会自动处理连接词:当 role'ADMIN' 时,内部 @{case} 输出为空,整个 @{and} 规则消失;当 role'MGR' 时,内部输出 dept_id = ?,外层自动加上 AND

这种自然的嵌套组合,让你能用 SQL 结构直接表达复杂的业务逻辑,无需在 Java 代码和 XML 配置文件之间反复切换。

无缝集成:规则无处不在

dbVisitor 动态规则的强大之处在于其普适性。你不需要为了使用它而改变现有的开发模式。

1. 在编程式 API 中使用

无需 StringBuilder 手动拼接字符串。

// 直接在 SQL 字符串中使用规则
jdbcTemplate.queryForList(
    "SELECT * FROM users @{and, name = :name}", args);

2. 在声明式注解中使用

彻底摆脱 @Script<script> 标签的包裹。

@Insert({"INSERT INTO users (",
         "  account, password",
         ") VALUES (",
         "  :account, @{md5, :password}",
         ")"})
int insertUser(User user);

3. 在传统 XML 文件中使用

如果你仍偏好独立管理 SQL,dbVisitor 也支持在 XML 文件中使用规则,完全替代 <if> 等标签。

<select id="queryUser">
    SELECT * FROM users
    @{and, name = :name}
    @{and, age = :age}
</select>

这意味着,你可以将简洁高效的动态 SQL 能力,带入任何你熟悉的技术栈和编码环境中。

总结

是时候进行一场直观的对比了:

dbVisitor与MyBatis XML动态SQL功能对比

技术在不断进步。MyBatis 在过去为 Java 生态做出了巨大贡献,但其处理动态 SQL 的 XML 方案在今天看来已略显陈旧和繁琐。

dbVisitor 通过引入脚本化的规则引擎,在保留 SQL 原生灵活性的同时,显著提升了开发效率和代码可读性。它证明了一个道理:强大的功能未必需要复杂的配置。

如果你已经厌倦了维护动辄上千行的 XML Mapper 文件,不妨尝试一下 dbVisitor,亲身体验告别“裹脚布”式编码后,那种畅快淋漓的开发感受。更多关于数据库和中间件的技术讨论,欢迎来云栈社区交流分享。




上一篇:生产级选择:VictoriaMetrics集群版部署Prometheus长期存储方案(附踩坑记录)
下一篇:逆势开源:Pigsty 4.0为何从AGPL转向Apache 2.0?
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-1-29 23:17 , Processed in 0.273891 second(s), 39 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表