Beetl 入门
About 2206 wordsAbout 7 min
1984-01-24
Beetl 语法类似js,java,如下做简要说明,使用可以参考 http://ibeetl.com , 或者在线体验 http://ibeetl.com/beetlonline/
BeetlSQL3默认使用Beetl语法来书写复杂SQL文件,BeetlSQL3也支持通过实现SQLTemplateEngine
来自定义模板引擎
定界符号
默认的定界符号是-- @
和 回车
。 里面可以放控制语句,表达式等语,,占位符号是#{}
,占位符号默认是输出?,并在执行sql的传入对应的值。如果想在占位符号输出变量值,使用${}
,或者在#{}
使用text函数
之所以考虑在BeetlSQL3中使用-- @
,是因为--
是sql注释符号,更少的破坏原有的SQL语句,可以直接把markdown的sql模板语句copy到数据库管理工具里使用
-- @if(!isEmpty(name)){
and name = #{name}
}
order by ${orderColumn}
输出变量值很常用,比如oder by,或者from后面的参数都不能是'?'. 因此必须使用${}.但需要防止SQL注入漏洞
如果想修改定界符,可以增加一个/btsql-ext.properties. 比如设置到BeetlSQL2的默认设置、
DELIMITER_PLACEHOLDER_START=#
DELIMITER_PLACEHOLDER_END=#
DELIMITER_STATEMENT_START=@
DELIMITER_STATEMENT_END=
DELIMITER_PLACEHOLDER_START2=${
DELIMITER_PLACEHOLDER_END2=}
beetlsql 的其他属性也可以在此文件里设置
需要确保${} 里不包含sql注入漏洞的语句
变量
通过程序传入的变量叫全局变量,可以在sql模板里使用,也可以定义变量,如
-- @ var count = 3;
-- @ var status = {"a":1} //json变量
算数表达式
同js,如a+1-b%30, i++ 等
select * from user where name like #{'%'+name+'%'}
逻辑表达式
有“&&”, “||”,还有 “!”,分别表示与,或,非, beetl也支持三元表达式
#{user.gender==1?'女':'男'}
控制语句
- if else: 这个同java,c,js。
- for: 循环语句,如for(id:ids){}
select * from user where status in (
-- @for(id in ids){
#{id} #{text(idLP.last?"":"," )}
-- @}
注意
- 变量名+LP 是一个内置变量,包含了循环状态,具体请参考beetl文档,text方法表示直接输出文本而不是符号“?”
- 关于 sql中的in,可以使用内置的join方法更加方便
- while 循环语句 ,如
while(i<count){}
访问变量属性
- 如果是对象,直接访问属性名,比如传给模板的对象名字是
user
, 则 user.name - 如果传入模板的对象的变量名是
_root
,则可以直接访问属性,比如name,而不需要_root.name - 如果是Map,用key访问 map["key"],map[xxxVarName]
- 如果是数组或者list,用索引访问,如list[1],list[i];
- 可以直采用java方式访问变量的方法和属性,如静态类Constatns
public class Constatns{
public static int RUNNING = 0;
public static User getUser(){}
}
直接以java方式访问,需要再变量符号前加上@,可以在模板里访问
select * from user where status = #{@Constatns.RUNNING} and id = #{Constatns.getUser().getId()}
注意,如果Constants 类 没有导入进beetl,则需要带包名,导入beetl方法是配置IMPORT_PACKAGE=包名.;包名.
判断对象非空(重要)
可以采用isEmpty判断变量变量是否存在,表达式是否为空(为null),如
if(isEmpty(user)||isEmpty(role.name))
如果user不存在,user为null返回true, 或者role不存在,role为null,或者role.name为null,也返回true。
isBlank 用于判断字符串是否为null或者为空
if(isBlank(name))
也可以用传统方法判断,如
if(user==null) or if(role.name!=null))
变量有可能不存在,可用has函数或者需要使用安全输出符号,如
if(null==user.name!))
//or
if(has(user))
变量表达式后面跟上"!" 表示如果变量不存在,则为!后面的值,如果!后面没有值,则为null
isEmpty和isNotEmpty函数经常被定制,结合isBlank语义,判断字符串是否为空,可以btsql-ext.properties文件里增加如下配置覆盖默认实现
FN.isEmpty=org.beetl.ext.fn.EmptyExpressionFunction
FN.isNotEmpty=org.beetl.ext.fn.IsNotEmptyExpressionFunction
你可以参考org.beetl.ext.fn.EmptyExpressionFunction 编写你自己对空的定义
调用方法
同js,唯一值得注意的是,在占位符里调用text方法,会直接输出变量而不是“?”,其他以db开头的方式也是这样。架构师可以设置SQLPlaceholderST.textFunList.add(xxxx) 来决定那些方法在占位符号里可以直接输出文本而不是符号"?"
beetl提供了很多内置方法,如print,debug,isEmpty,date等,具体请参考文档
自定义方法
通过配置btsql-ext.properties, 可以注册自己定义的方法在beetlsql里使用,如注册一个返回当前年份的函数,可以在btsql-ext.properties加如下代码
FN.db.year= com.xxx.YearFunction
这样在模板里,可以调用db.year()
获得当前年份。YearFunction 需要实现Function的 call方法,如下是个简单代码
public class YearFunction implements Function{
public String call(Object[] paras, Context ctx){
return "2015";
}
}
关于如何完成自定义方法,请参考 ibeetl 官方文档
内置方法
- print、println :输出,同js,如print("table1");
- has : 判断是否有此全局变量;
- isEmpty : 判断表达式是否为空,不存在,空字符串,空集合都返回true;
- debug :将变量输出到控制台,如 debug(user);
- text :输出变量值本身,但可用于占位符号里
- page :分页函数,用于在PageQuery翻页里,根据上下问决定输出count(1) 或者count(*),如果有参数,则按照参数输出
- join :将集合或数组内元素用逗号拼接,并输出
?
用于占位符,用于in,如
select * from user where status in ( #{join(ids)})
-- 输出成 select * from user where status in (?,?,?)
- use 参数是同一个md文件的sqlid,类似mybatis的 sql功能,如
condtion
===
where 1=1 and name = #{name}
selectUser
===
select * from user #{use("condition")}
- globalUse 参数是其他sql文件的Sql片段的Id,如globalUse("share.accessControl"),将访问share.md(sql)文件的accessControl片段
use和globalUse 都允许第二个json参数,传递更多的变量
- db.dynamicSql 类似use功能,但第一个参数是sql片段,而不是sqlId
queryUsers
===
-- @ var sql = "id=#{xxx}";
select #{page("*")} from user where 1=1 and #{db.dynamicSql(sql,{xxx:1\})}
注意,
db.dynamicSql(sql,{xxx:1\}
beetl语法解析#{} 中的字符的时候,如果出现了}
需要在前面加一个\
- page函数用于PageQuery,但beetlsql 使用PageQuery查询,会将sql模板翻译成带有count(1),以及指定列名的俩个sql语句,因此必须使用page函数或者pageTag标签
queryNewUser
===
select #{page()} from user
如果无参数,则在查询的时候解释成 *
,如果有参数则解释成给定的列名,如 page("a.name,a.id,b.name role_name") ,如果列名较多,可以使用pageTag
注意 page函数只是辅助生成count(1) ,如果sql语句是带有group by,则需要嵌套子查询才能正确翻页
标签功能
- beetlsql 提供了trim标签函数,用于删除标签体最后一个逗号,这可以帮助拼接条件sql,如
updateStatus
===
update user set
-- @trim(){
-- @if(!isEmpty(age){
age = #{age} ,
-- @} if(!isEmpty(status){
status = #{status},
-- @}
-- @}
where id = #id#
trim 标签可以删除标签体里的最后一个逗号。也可以实现类似mybatis的trim标签的功能,通过传入trim参数prefix,prefixOverrides来完成。具体参考标签api 文档
- pageTag :同page函数,用于pageQuery,如
queryNewUser
===
select
-- @pageTag(){
id,name,status
-- @}
from user
注:可以参考beetl官网 了解如何开发自定义标签以及注册标签函数
- pageIgnoreTag,该标签的作用是在生成分页查询的count语句时,忽略sql语句里的某些内容,如:order by 。pageIgnoreTag与pageTag标签组合使用,组合如下
queryNewUser
===
select
-- @pageTag(){
id,name,status
-- @}
from user
-- @pageIgnoreTag(){
order by a.createTime
-- @}
因为count语句,无需要排序语句部分,而且,有些数据库,如SQLServer并不支持count语句被排序,因此可以使用pageIgnoreTag来解决夸数据库问题。 而且忽略order by 也会带来性能提升。
- where
该标签复用TrimTag,其工作过程是:判断where 里的sql内容是否为空,如果为空就不输出空字符串,如果不为空则判断sql是否以AND或OR开头,如果是,则去掉。例如模板内容如下:
queryNewUser
===
select a.* from user a
-- @ where(){
-- @ if(!isEmpty(age){
and a.age=#{age}
-- @}
-- @ if(!isEmpty(status){
and a.status=#{status}
-- @}
-- @}
将生成
select a.* from user a
where
a.age=?
and a.status=?
当然,如果你不用where,也可用where 1=1 来解决,性能并没有不同
附录
Beetl提供的内置函数和实现类
## 内置的方法
FN.date = org.beetl.ext.fn.DateFunction
FN.nvl = org.beetl.ext.fn.NVLFunction
FN.debug = org.beetl.ext.fn.DebugFunction
#兼容以前版本,用has代替
FN.exist = org.beetl.ext.fn.CheckExistFunction
FN.has = org.beetl.ext.fn.CheckExistFunction
FN.printf = org.beetl.ext.fn.Printf
FN.decode = org.beetl.ext.fn.DecodeFunction
FN.assert = org.beetl.ext.fn.AssertFunction
FN.print = org.beetl.ext.fn.Print
FN.println = org.beetl.ext.fn.Println
FN.printFile = org.beetl.ext.fn.PrintFile
FN.trunc = org.beetl.ext.fn.TruncFunction
FN.trim = org.beetl.ext.fn.TruncFunction2
FN.qmark = org.beetl.ext.fn.QuestionMark
FN.isEmpty = org.beetl.ext.fn.EmptyExpressionFunction
FN.isNotEmpty = org.beetl.ext.fn.IsNotEmptyExpressionFunction
FN.parseInt = org.beetl.ext.fn.ParseInt
FN.parseLong = org.beetl.ext.fn.ParseLong
FN.parseDouble= org.beetl.ext.fn.ParseDouble
FN.range = org.beetl.ext.fn.Range
FN.flush = org.beetl.ext.fn.Flush
FN.json = org.beetl.ext.fn.Json
FN.pageCtx = org.beetl.ext.fn.PageContextFunction
FN.type.new=org.beetl.ext.fn.TypeNewFunction
FN.type.name=org.beetl.ext.fn.TypeNameFunction
FN.global=org.beetl.ext.fn.DynamicGlobalValueFunction
FN.allGlobal=org.beetl.ext.fn.AllGlobaAsJsonlFunction
FN.hasAttribute=org.beetl.ext.fn.HasAttributeFunction
FN.env=org.beetl.ext.fn.EnvFunction
FN.parentTag=org.beetl.ext.fn.ParentTagFunction
BeetlSQL 内置方法
FN.use = org.beetl.sql.core.engine.UseFunction
FN.globalUse = org.beetl.sql.core.engine.GlobalUseFunction
FN.text = org.beetl.sql.core.engine.TextFunction
FN.join = org.beetl.sql.ext.JoinFunction
FN.isEmpty=org.beetl.sql.core.engine.EmptyExpressionFunction
FN.isNotEmpty=org.beetl.sql.core.engine.NotEmptyExpressionFunction
FN.isBlank=org.beetl.sql.core.engine.IsBlank
FN.db.dynamicSql=org.beetl.sql.core.engine.DynamicSqlFunction
FN.page=org.beetl.sql.core.engine.PageQueryFuntion
FN.jsonMapping=org.beetl.sql.core.engine.MappingFunction
TAG.trim= org.beetl.sql.core.engine.TrimTag
TAG.pageTag= org.beetl.sql.core.engine.PageQueryTag
TAG.pageIgnoreTag= org.beetl.sql.core.engine.PageQueryIgnoreTag
TAG.where= org.beetl.sql.core.engine.WhereTag
## 内部使用
FN.db.testNull=org.beetl.sql.core.engine.TestNullFunction
FN.db.testColNull=org.beetl.sql.core.engine.TestColNullFunction