动态 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 计算结果,配置数据源的情况下,甚至可以在测试库执行看到具体的查询效果,在正式上线前就可以避免很多问题!