跳到主要内容

7.1 语句生成规则

SQL 增强规则用于简化动态 SQL 开发。相比 XML 标签,规则能更智能地处理空参数、连接符(AND/OR)和逗号分隔符。

规则描述
@{and} / @{ifand}智能追加 AND 条件。
@{or} / @{ifor}智能追加 OR 条件。
@{in} / @{ifin}展开集合参数为 IN (v1, v2)
@{set} / @{ifset}智能处理 UPDATE 语句的 SET 逗号。
@{if} / @{iftext}通用条件判断(解析 / 直出)。
@{text}原样输出文本片段(不解析参数)。
@{case} / @{when} / @{else}多路分支 (Switch / If-Else)。
@{macro} / @{ifmacro}引用预定义的 SQL 宏片段。
@{md5}计算参数 MD5 值。
@{uuid32} / @{uuid36}生成 UUID。
@{pairs}遍历 Map/List 生成参数模版。

AND、IFAND 规则

解决 WHERE 子句中动态拼接 AND 条件的问题。

  • @{and, sql片段}:当 SQL 片段中的参数非空(或包含 ${...} 注入)时,自动追加该片段。
  • @{ifand, OGNL条件, sql片段}:当 OGNL 条件表达式为真时,追加 SQL 片段(即使参数值为 null 也会保留)。

and 与 ifand 的区别

特性@{and, sql片段}@{ifand, OGNL条件, sql片段}
生效判断SQL 片段中的至少一个参数非 null 时生效(文本注入另见下文)OGNL 条件表达式true 时生效
空值参数参数全部为 null 或没有参数时,整个片段被丢弃条件满足时参数为 null 也会保留
典型用途简洁写法,自动判空需要自定义判断逻辑

特性说明

  1. 上下文连接符:规则检查前文末尾的 WHEREANDOR 等,决定是否补连接符;规则内容应直接写条件,例如 WHERE @{and, x=:x}。不要在规则内部再写前导 AND。
  2. 智能空值丢弃
    • 默认情况下,如果 @{and, col=:val} 中的 :val 为空 (null),则整个片段会被丢弃。
    • 例外:如果片段中包含 ${...} 动态注入,即使参数为空,该片段也会被强制保留(防止误删筛选逻辑)。

示例

示例status 必填,userId 选填。无需手动处理 WHERE/AND 连接符。

select * from users
where status = :status -- 固定条件
@{and, uid = :userId} -- 自动处理 AND 前缀
-- 若需额外检查长度,可将上面规则替换为:
-- @{ifand, userId != null && userId.length() > 0, uid = :userId}
生成的 SQL (status=1, userId='abc')
select * from users
where status = ? and uid = ?
智能特性
  • 自动补全 WHERE:若规则位于条件首位,会自动补全 WHERE 关键字。
  • 按上下文补连接符:检查前文决定是否添加 AND;不会去除规则内容中手写的前导 AND
  • 保留已有连接符:若前文已以 OR 结尾,不再补 AND

OR、IFOR 规则

解决 WHERE 子句中动态拼接 OR 条件的问题。与 AND 规则对称。

  • @{or, sql片段}:当 SQL 片段中的参数非空时,自动追加该片段。
  • @{ifor, OGNL条件, sql片段}:当 OGNL 条件为真时,追加 SQL 片段(即使参数为 null)。

示例:匹配用户名或邮箱。自动处理 OR 连接符。

select * from users
where username = :username
@{or, email = :email} -- 自动以 OR 连接
-- 如需检查格式,可将上面规则替换为:
-- @{ifor, email != null && email.contains("@"), email = :email}
生成的 SQL (username='admin', email='a@b.c')
select * from users
where username = ? or email = ?
智能特性

与 AND 规则一致,根据前文补 WHEREOR;规则内部应直接写条件,不要再写前导连接符。


IN、IFIN 规则

用于简化 IN 子句的拼接,自动展平集合参数。支持 List、数组(包括原始类型数组如 int[])。

  • @{in, :param}:自动将集合/数组参数展平为 (?, ?, ...)
  • @{ifin, OGNL条件, :param}:当 OGNL 条件为真时生效。

示例:查询 ID 在列表中的用户。

select * from users
where status = :status
@{in, and id in :ids} -- 自动展开为 id in (?,?,?)
-- 如需显式判断,可将上面规则替换为:
-- @{ifin, ids != null && ids.size() > 0, and id in :ids}
生成的 SQL (ids=[1,2,3])
select * from users
where status = ? and id in (?, ?, ?)
注意事项

本规则不会自动补全 AND/OR 前缀,需在规则内手动写明(如示例中的 and id in ...)。集合为空或 null 时规则不输出内容;若空集合应表示“不匹配任何行”,应在调用前返回空结果或显式追加 1=0,不要让条件被省略后扩大查询或修改范围。


SET、IFSET 规则

专用于 UPDATE 语句,解决动态列更新时的逗号拼接问题。

  • @{set, sql片段}:追加赋值并自动管理逗号。与 and/or 不同,set 允许参数值为 null
  • @{ifset, OGNL条件, sql片段}:当 OGNL 条件为真时生效。

示例:更新用户,status 为 null 时也会写入 NULL。

update users
set update_time = now() -- 固定列
@{set, status = :status} -- 自动处理逗号
-- 如需按条件更新,可将上面规则替换为:
-- @{ifset, status != null && status != 'disabled', status = :status}
where uid = :uid
生成的 SQL (status='Active')
update users
set update_time = now(), status = ?
where uid = ?

使用技巧

当在 @{set} 规则之间混合使用手动 SQL(例如 fixed_col = 1)时,无需手动添加逗号。 规则引擎会自动检测前文内容并智能补充逗号。手动添加逗号反而可能在某些动态场景下导致语法错误。

推荐写法(规则放到最后)
update users set
fixed_col = 1 -- 结尾不要加逗号
@{set, name = :name} -- 规则会自动处理前置逗号(生成 ", name = ?")
@{set, age = :age}
where id = :id
问题写法(当规则没有匹配时)
UPDATE tb_user SET
@{set, name = :name}, -- ❌ 规则无法删除身后的逗号
fixed_col = 123,
@{set, email = :email} -- ❌ 规则虽然不会添加新的逗号但也不会删除上一个条件中的逗号
WHERE id = :id

IF、IFTEXT 规则

通用条件判断规则。当 test 表达式为真时,将 content 包含在最终 SQL 中。

虽然功能类似,但两者对 content 内容的处理方式截然不同:

  • @{if, test, content}智能解析。会对 content 进行完整解析,支持嵌套其他动态规则(如 @{in})、参数占位符等。这是最常用的方式。
  • @{iftext, test, content}原生直出。不对 content 做任何解析,直接原样拼接到 SQL 中。用于注入特殊关键字或不支持参数化的语法片段。
select * from users where 1=1
-- 1. 普通 @{if}:支持解析内部的 @{in} 和参数 :name
@{if, hasName, and name = :name}
@{if, idList != null, and id in @{in, :idList}}

-- 2. 原生 @{iftext}:原样注入 SQL 片段(不解析参数)
@{iftext, status > 2, and age = 36 }
生成的 SQL (hasName=true, idList=[1,2], status=3)
select * from users where 1=1
and name = ? -- @{if} 正常解析参数
and id in (?, ?) -- @{if} 允许嵌套 @{in}
and age = 36 -- @{iftext} 原样拼接

TEXT 规则

原样输出文本片段,不解析其中的参数占位符或嵌套规则。

  • @{text, content}:将 content 原样拼接到 SQL 中。
示例
select * from users where 1=1
@{text, and status = 'active'}
生成的 SQL
select * from users where 1=1
and status = 'active'
与 iftext 的区别
  • @{text, content} 无条件输出,等价于 @{iftext, , content}
  • @{iftext, test, content} 需要 test 条件为真才输出。

CASE、WHEN、ELSE 规则

提供 SQL 生成阶段的分支逻辑,支持 Switch (值匹配) 和 If-Else (条件匹配) 两种模式。

值匹配@{case} 第一个参数为变量。

select * from users where @{case, userType,
@{when, 'admin', role = 'administrator'},
@{when, 'manager', role = 'manager'},
@{else, role = 'visitor'}
}
生成的 SQL (userType='admin')
select * from users
where role = 'administrator'
使用须知
  • 模式切换:通过 @{case} 第一个参数是否存在来切换 Switch / If-Else 模式。
  • Switch 值匹配@{when, val}val 会通过 OGNL 求值后,与 @{case, expr} 的求值结果进行 equals() 比较。不同类型(如 Integer(1) vs "1")会通过 String.valueOf() 回退匹配。
  • Else@{else} 必须写在最后。
  • 默认值:若无匹配且无 else,输出空字符串。

MACRO、IFMACRO 规则

用于将预先定义的 SQL 片段(宏)包含进最终 SQL 中,类似于 XML 映射文件中的 <include> 标签。

  • @{macro, name}:引用名称为 name 的 SQL 宏。
  • @{ifmacro, test_expr, name}:当 test_expr 为真时,引用名称为 name 的 SQL 宏。

1. 注册宏 (Java)

// 通过 Configuration 注册
Configuration config = new Configuration();
config.addMacro("includeSeq", "and seq = :seq");

2. 引用宏 (SQL)

select * from users where
status = :status
@{macro, includeSeq} -- 引用宏
-- 如需按条件引用,可替换上面规则:
-- @{ifmacro, status > 2, includeSeq}
生成的 SQL (status=3)
select * from users
where status = ? and seq = ?
注意事项

引用一个不存在的 SQL 宏会导致执行报错。

XML 中定义宏

在 Mapper XML 文件中使用 <sql id="xxx"> 定义的片段会自动注册为宏,命名规则为 namespace.id。 因此也可以通过 @{macro, namespace.sqlId} 在规则中引用 XML 定义的 SQL 片段。


MD5 规则

对参数值进行 MD5 哈希计算,并将结果作为 SQL 参数绑定。

  • @{md5, :param}:取 :param 参数的值,计算其 MD5 后绑定为 ? 占位符。
注意

规则内容必须解析出且仅解析出一个绑定参数,如 :loginPassword#{loginPassword}?;裸名称不是绑定参数。MD5 是哈希而非加密,不应用于新的密码存储方案;下面仅演示兼容既有 MD5 字段。

示例:对既有 MD5 密码字段进行哈希匹配。

select * from users
where account = :loginName
and password = @{md5, :loginPassword}
生成的 SQL
select * from users
where account = ? and password = ? -- 参数值为 MD5(loginPassword)

UUID 规则

自动生成 UUID 并作为 SQL 参数。

  • @{uuid32}:生成 32 长度 UUID (无-分隔符)。
  • @{uuid36}:生成 36 长度 UUID (带-分隔符)。

示例:插入时自动生成 ID。

insert into users (id, uid, name, time)
values (:id, @{uuid32}, :name, now());
生成的 SQL
insert into users (id, uid, name, time)
values (?, ?, ?, now()); -- 第二个参数为生成的 UUID

PAIRS 规则

用于遍历集合(Map/List/Array)并按模版生成 SQL 片段。

  • @{pairs, :collection, template}:遍历 :collection 参数,对每个元素应用 template

模版变量(固定名称,不可配置):

变量Map 场景List/Array 场景
:kMap 的 Key元素索引(字符串形式,如 "0""1"
:vMap 的 Value元素值
:i迭代序号(从 0 开始)迭代序号(从 0 开始)

示例:将 Map 数据写入 Redis HASH 结构。

-- 假设参数 arg0 是一个 Map: {"field1": "val1", "field2": "val2"}
HSET myKey1 @{pairs, :arg0, :k :v}
生成的命令
HSET myKey1 field1 val1 field2 val2