动态 SQL 流程控制实用小技巧
在框架中,其最核心的动态 SQL 除了和常规语言具有几乎相同的逻辑以外,可玩性不止于此,根据一些实战来获得更多的了解。
Check 条件检查语句
在 SQL 到达数据库之前,对 SQL 的参数做一次合法性验证,并提前抛出异常,而不是等数据库来抛出异常,避免获取一次无效的 connection 对象。
特别是在无法确定这条 SQL 会在几个地方以不同的方式调用,以免程序代码中无法做到一致性的参数校验,此时 check 就发挥作用了,统一校验。
-- #check :id == null throw 'ID不能为null'
-- #check :start | type != 'Date' throw '开始时间类型必须是Date'
select * from table where ...
Switch 语法讲解
在 Rabbit SQL 框架动态 SQL 中,switch 作用类似于 java 代码的 switch 语法,在 java 中,case 多个值的写法是这样的:
switch(value){
case "cyx":
case "mike":
case "bob":
// ....
break;
}
同样的 rabbit sql 也支持,只是写法略有不同,多个值写在同一个 case 里面,用逗号分隔:
select * from user t where t.id = :id
-- #switch :name
-- #case cyx, mike, 'bob'
and t.name = :name
-- #break
-- #end
需要特别说明过一下,如果值不是纯数字和关键字(
null,blank,true,false)的话,可以不需要加引号,默认为字符串,'bob'的引号不是必要的。
switch 同样支持管道处理待比较的值:
select * from user t where t.id = :id
-- #switch :name | length
-- #case 3
...
-- #break
-- #case 4
...
-- #break
-- #end
length是内置管道,管道语法类似于 shell 中的管道功能,| 操作符1 | 操作符2,可以连续写多个,可以根据需求实现更多功能的自定义管道。
for 循环技巧
最初实现 for 循环的目的为解决 SQL in 语句预编译的问题,为了预编译参数尽可能覆盖到所有地方,例如有一个 List("cyx", "mike", "bob") ,这个参数需要用在 in 语句:
select * from user where id = :id
or name in
这里可以利用字符串模版占位符来实现:${!names} :
select * from user where id = :id
or name in (${!names})
字符串模版占位符支持集合,前面的
!表明会对集合进行处理,如果不是数字,会自动加引号变为... name in ('cyx', 'mike', 'bob')
但这并不够好,这是一个取巧的方式,字符串模版可能无法完全覆盖字符串的安全处理,最好的方式还是使用 for 循环来构建:
select * from user where id = :id
or name in (
-- #for name of :names; first as isFirst
-- #if :isFirst
:name,
-- #else
:name
-- #fi
-- #done
)
最终执行的 SQL 的 in 语句部分为:in (?, ?, ?) 达到了预编译的效果,当然 for 的功能不止于此,具体可参考 for 循环语法详解。
插件支持
根据流程控制语法规则,动态 SQL 可以互相嵌套,但过于复杂的动态 SQL 我们不可能等到正式环境再来测试效果,这显然是不科学的。
虽然框架本身支持动态 SQL 语法检查,但也不可能每次写完动态 SQL 重启项目,这是低效的,我们可以使用插件来测试动态 SQL,我敢说这可能是你用过所有 SQL 框架中最好的测试工具,无论 UI 还是效果和 IDEA 融为一体,就好像是 IDEA 内置功能:

插件可自动识别 SQL 中的参数,没有数据源的情况下,可以直接测试动态 SQL 计算结果,配置数据源的情况下,甚至可以在测试库执行看到具体的查询效果,在正式上线前就可以避免很多问题!