6.3.1 语句生成规则
以下示例展示 SQL 片段及生成的 SQL,绑定参数按 ? 出现顺序列出;换行和空格已规整。完整 DataQL 调用方式见动态规则。
AND、IFAND
@{and, 条件} 根据绑定值选择是否输出条件,并补充 WHERE 或 AND;@{ifand, OGNL表达式, 条件} 由表达式决定是否输出。
SELECT * FROM people WHERE enabled = 1
@{and, name = #{name}}
@{ifand, minAge != null and minAge >= 0, age >= #{minAge}}
传入 name = "Alice"、minAge = 18:
SELECT * FROM people WHERE enabled = 1 AND name = ? AND age >= ?
-- 参数:["Alice", 18]
and的绑定参数全部为 null,或内容没有绑定参数时,省略该片段。空字符串、0、false 都是非 null 值。- 一个片段有多个参数时,只要其中一个非 null,就保留整个片段及其他 null 参数。例如
@{and, age BETWEEN #{low} AND #{high}}在low = 18, high = null时仍输出两个占位符。 - 内容直接含有
${...}文本替换时,and不会因为绑定参数全部为 null 而省略片段。文本替换规则见SQL 参数。 ifand的条件为 true 时,即使没有参数或参数为 null,也输出内容。判断非空字符串应显式写出name != null and name != ''。
规则检查前文是否包含 where,以及末尾是否为 where、and、or 等,再决定补充连接符。内容直接写 name = #{name},不要重复写前导 AND。复杂子查询建议显式写 WHERE 1 = 1,由规则追加条件。
固定条件使用 @{ifand, true, enabled = 1},或直接写 SQL;@{and, enabled = 1} 没有绑定值,会被省略。
OR、IFOR
or、ifor 的空值处理与 and、ifand 相同,连接符改为 OR。
SELECT * FROM people WHERE name = #{name}
@{or, id = #{id}}
传入 name = "Alice"、id = 2:
SELECT * FROM people WHERE name = ? OR id = ?
-- 参数:["Alice", 2]
传入 id = null 时只保留姓名条件。需要显式判断时写 @{ifor, id != null, id = #{id}}。混合 AND、OR 时,SQL 运算优先级不变,需要分组的条件应自行添加括号,例子见规则嵌套。
SET、IFSET
@{set, 赋值} 添加 SET 或赋值之间的逗号,保留 null 值。@{ifset, 条件, 赋值} 只在条件为 true 时输出。
UPDATE people
@{set, name = #{name}}
@{ifset, changeAge, age = #{age}}
WHERE id = #{id}
传入 name = "Alice"、changeAge = true、age = null、id = 1:
UPDATE people SET name = ?, age = ? WHERE id = ?
-- 参数:["Alice", null, 1]
changeAge = false 时不更新 age。set 不会跳过 null 赋值,适合将列清空;只更新非空值时使用 ifset。
逗号由规则补在赋值之前,不要在规则后手动加逗号。全部赋值都被条件排除时,不会自动生成有效的 UPDATE;应保留必需赋值或在脚本中跳过此次更新。
IF、TEXT、IFTEXT
@{if, 条件, 内容} 在条件为 true 时解析内容中的参数和嵌套规则,不自动添加 SQL 连接符。
SELECT * FROM people WHERE 1 = 1
@{if, name != null, AND name = #{name}}
@{iftext, newest, ORDER BY id DESC}
传入 name = "Alice"、newest = true:
SELECT * FROM people WHERE 1 = 1 AND name = ? ORDER BY id DESC
-- 参数:["Alice"]
text 无条件原样输出,iftext 判断条件后原样输出:
SELECT * FROM people @{text, ORDER BY id ASC}
生成 SELECT * FROM people ORDER BY id ASC,无参数。text、iftext 中的 #{...}、${...} 和嵌套规则不会再次解析;需要绑定值时使用 if。这些规则适合脚本中的固定 SQL 关键字,不负责把请求内容转换为 SQL。
所有 if* 规则的条件为空时视为成立;非空条件必须求值为布尔 true。数字 1 或字符串 "true" 不能替代布尔值。
IN、IFIN
@{in, SQL片段} 将片段内的一个绑定参数展开成括号包围的占位符列表;ifin 先检查条件。规则不添加 AND、OR。
SELECT * FROM people WHERE enabled = 1
@{in, AND id IN #{ids}}
传入 ids = [1, 2, 3]:
SELECT * FROM people WHERE enabled = 1 AND id IN (?, ?, ?)
-- 参数:[1, 2, 3]
条件写法为 @{ifin, ids != null and ids.size() > 0, AND id IN #{ids}}。集合、数组均可展开;非空标量生成一个占位符。已有的参数 JDBC 选项会应用到各元素,例如 #{ids, jdbcType=INTEGER}。
- null 或空集合会省略整个规则,已有的固定条件继续执行。
- 集合中的 null 保留为参数,数据库按 SQL NULL 规则比较。
- 内容必须只产生一个绑定参数;没有参数时不输出,多个参数时抛出异常。
- 不要再为
#{ids}添加括号,规则本身会生成括号。
如果空列表表示“不返回任何记录”,应先在 DataQL 中返回空列表,或通过分支输出 AND 1 = 0;不要把省略条件当作空结果。
CASE、WHEN、ELSE
case 选择首个匹配分支。它在语句生成时执行,只把选中的内容发送到数据库。
条件模式将 case 的表达式留空:
SELECT * FROM people WHERE 1 = 1
@{case, ,
@{when, name != null, AND name = #{name}}
@{when, minAge != null, AND age >= #{minAge}}
@{else, AND enabled = 1}
}
name = "Alice", minAge = 18 时仅输出 AND name = ?,参数为 ["Alice"];两者都为 null 时输出 AND enabled = 1,没有绑定参数。
值模式比较 case 的表达式与 when 的值:
SELECT * FROM people
@{case, sort,
@{when, 'name', ORDER BY name, id}
@{when, 'age', ORDER BY age, id}
@{else, ORDER BY id}
}
sort = "age" 时生成 SELECT * FROM people ORDER BY age, id。分支值是 OGNL 表达式,固定字符串需加引号。值模式先比较对象相等,再比较字符串表示;例如数字 1 与字符串 "1" 可以匹配。
else 放在最后;没有匹配且没有 else 时输出为空。when、else 只能写在 case 中。case 只处理直接子级的 when、else,SQL 正文应写在分支内部。分支内容支持继续嵌套规则。
MACRO、IFMACRO
macro 引用应用注册的公共 SQL 片段。假设已将 activePeople 注册为 AND enabled = 1:
SELECT * FROM people WHERE age >= #{minAge}
@{macro, activePeople}
传入 minAge = 18,生成 SELECT * FROM people WHERE age >= ? AND enabled = 1,参数为 [18]。条件引用写作 @{ifmacro, activeOnly, activePeople};条件为 false 时不查找该片段。
名称直接写注册名,不使用引号或 #{...}。引用内容会解析参数和其他规则,并共享当前片段参数。片段未注册时执行报错。完整注册和 XML 引用方式见SQL 片段。
MD5
md5 将一个绑定参数转成文本,计算摘要,再作为 VARCHAR 参数绑定。
INSERT INTO payload_log(digest) VALUES (@{md5, #{content}})
传入 content = "abc":
INSERT INTO payload_log(digest) VALUES (?)
-- 参数:["900150983cd24fb0d6963f7d28e17f72"]
null 按空字符串计算,得到 d41d8cd98f00b204e9800998ecf8427e。规则必须只产生一个绑定参数,裸写 content 不会绑定参数。此规则适合兼容已有摘要字段;MD5 不适合新设计的密码存储。
UUID32、UUID36
两个规则均生成新的 VARCHAR 参数:uuid32 长度为 32,不含连字符;uuid36 长度为 36,包含连字符。
INSERT INTO event_log(id, correlation_id, message)
VALUES (@{uuid32}, @{uuid36}, #{message})
生成 VALUES (?, ?, ?),参数依次为两个新 UUID 和 message。每次执行重新生成,不接收业务参数;生成值不会自动写回 DataQL 变量。
ARG
#{...} 是参数配置的常用写法;arg 提供对应规则入口,第二段留空,第三段为参数表达式和选项:
SELECT * FROM people WHERE id = @{arg, , id, jdbcType=INTEGER}
传入 id = 1,生成 WHERE id = ?,绑定一个 JDBC INTEGER 参数。它等价于 #{id, jdbcType=INTEGER}。null 仍生成占位符,指定 JDBC 类型有助于驱动绑定 null。mode、name、scale、typeName、typeHandler 详见参数选项。
PAIRS
@{pairs, #{集合}, 模板} 遍历对象、列表或数组。每次循环使用固定局部变量:
k:对象字段名,或列表/数组下标的字符串表示。v:当前值。i:从 0 开始的整数序号。
这些变量仅在当前模板中覆盖同名参数,模板仍可读取外部参数。各轮按对象迭代顺序或列表顺序连接,分隔符是空白,不会自动加逗号。
@{pairs, #{names},
@{iftext, i > 0, UNION ALL}
SELECT CAST(#{v} AS VARCHAR) AS name
}
CAST 为 UNION 查询中的绑定参数明确 VARCHAR 类型。
传入 names = ["Alice", "Bob"]:
SELECT CAST(? AS VARCHAR) AS name UNION ALL SELECT CAST(? AS VARCHAR) AS name
-- 参数:["Alice", "Bob"]
需要逗号时,在模板中使用 @{iftext, i > 0, ,} 输出分隔符,或使用提供 separator 的 foreach 标签。null 和空集合不输出内容;非对象、非集合、非数组的标量会报错。模板中的值使用 #{v} 绑定;动态列名必须由应用白名单控制。