### Beetl 入门 Beetl 语法类似js,java,如下做简要说明,使用可以参考 [http://ibeetl.com](http://ibeetl.com/) , 或者在线体验 [http://ibeetl.com/beetlonline/](http://ibeetl.com/beetlonline/) BeetlSQL3默认使用Beetl语法来书写复杂SQL文件,BeetlSQL3也支持通过实现`SQLTemplateEngine`来自定义模板引擎 #### 定界符号 默认的定界符号是`-- @` 和 `回车`。 里面可以放控制语句,表达式等语,,占位符号是`#{}`,占位符号默认是输出?,并在执行sql的传入对应的值。如果想在占位符号输出变量值,使用`${}`,或者在`#{}`使用text函数 之所以考虑在BeetlSQL3中使用`-- @`,是因为`--` 是sql注释符号,更少的破坏原有的SQL语句 ``` -- @if(!isEmpty(name)){ and name = #{name} } order by ${orderColumn} ``` > 输出变量值很常用,比如oder by,或者from后面的参数都不能是'?'. 因此必须使用${}.但需要放置SQL注入漏洞 如果想修改定界符,可以增加一个/btsql-ext.properties. 比如设置到BeetlSQL2的默认设置、 ```properties DELIMITER_PLACEHOLDER_START=# DELIMITER_PLACEHOLDER_END=# DELIMITER_STATEMENT_START=@ DELIMITER_STATEMENT_END= DELIMITER_PLACEHOLDER_START2=${ DELIMITER_PLACEHOLDER_END2=} ``` beetlsql 的其他属性也可以在此文件里设置 > 需要确保${} 里不包含sql注入漏洞的语句 #### 变量 通过程序传入的变量叫全局变量,可以在sql模板里使用,也可以定义变量,如 ```javascript -- @ var count = 3; -- @ var status = {"a":1} //json变量 ``` #### 算数表达式 同js,如a+1-b%30, i++ 等 ```sql select * from user where name like #{'%'+name+'%'} ``` #### 逻辑表达式 有“&&”, “||”,还有 “!”,分别表示与,或,非, beetl也支持三元表达式 ```javascript #{user.gender==1?'女':'男'} ``` #### 控制语句 - if else: 这个同java,c,js。 - for: 循环语句,如for(id:ids){} ```markdown 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"]; - 如果是数组或者list,用索引访问,如list[1],list[i]; - 可以直采用java方式访问变量的方法和属性,如静态类Constatns ```java public class Constatns{ public static int RUNNING = 0; public static User getUser(){} } ``` 直接以java方式访问,需要再变量符号前加上@,可以在模板里访问 ```sql select * from user where status = #{@Constatns.RUNNING} and id = #{Constatns.getUser().getId()} ``` 注意,如果Constants 类 没有导入进beetl,则需要带包名,导入beetl方法是配置IMPORT_PACKAGE=包名.;包名. #### 判断对象非空(重要) 可以采用isEmpty判断变量变量是否存在,表达式是否为空(为null),如 ```javascript if(isEmpty(user)||isEmpty(role.name)) ``` 如果user不存在,user为null返回true, 或者role不存在,role为null,或者role.name为null,也返回true。 isBlank 用于判断字符串是否为null或者为空 ``` if(isBlank(name)) ``` 也可以用传统方法判断,如 ```javascript if(user==null) or if(role.name!=null)) ``` 变量有可能不存在,可用has函数或者需要使用安全输出符号,如 ```javascript if(null==user.name!)) //or if(has(user)) ``` 变量表达式后面跟上"!" 表示如果变量不存在,则为!后面的值,如果!后面没有值,则为null isEmpty和isNotEmpty函数经常被定制,结合isBlank语义,判断字符串是否为空,可以btsql-ext.properties文件里增加如下配置覆盖默认实现 ```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加如下代码 ```properties FN.db.year= com.xxx.YearFunction ``` 这样在模板里,可以调用`db.year()` 获得当前年份。YearFunction 需要实现Function的 call方法,如下是个简单代码 ```java 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,如 ```sql select * from user where status in ( #{join(ids)}) -- 输出成 select * from user where status in (?,?,?) ``` - use 参数是同一个md文件的sqlid,类似mybatis的 sql功能,如 ```markdown 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 ```markdown queryUsers === -- @ var sql = "id=#{xxx}"; select #{page("*")} from user where 1=1 and #{db.dynamicSql(sql,{xxx:1})} ``` - page函数用于PageQuery,但beetlsql 使用PageQuery查询,会将sql模板翻译成带有count(1),以及指定列名的俩个sql语句,因此必须使用page函数或者pageTag标签 ```markdown queryNewUser === select #{page()} from user ``` 如果无参数,则在查询的时候解释成 `*`,如果有参数则解释成给定的列名,如 page("a.name,a.id,b.name role_name") ,如果列名较多,可以使用pageTag > 注意 page函数只是辅助生成count(1) ,如果sql语句是带有group by,则需要嵌套子查询才能正确翻页 #### 标签功能 - beetlsql 提供了trim标签函数,用于删除标签体最后一个逗号,这可以帮助拼接条件sql,如 ```markdown 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,如 ```markdown queryNewUser === select -- @pageTag(){ id,name,status -- @} from user ``` 注:可以参考beetl官网 了解如何开发自定义标签以及注册标签函数 - pageIgnoreTag,该标签的作用是在生成分页查询的count语句时,忽略sql语句里的某些内容,如:order by 。pageIgnoreTag与pageTag标签组合使用,组合如下 ```markdown 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开头,如果是,则去掉。例如模板内容如下: ```markdown queryNewUser === select a.* from user a -- @ where(){ -- @ if(!isEmpty(age){ and a.age=#{age} -- @} -- @ if(!isEmpty(status){ and a.status=#{status} -- @} -- @} ``` 将生成 ```sql select a.* from user a where a.age=? and a.status=? ``` 当然,如果你不用where,也可用where 1=1 来解决,性能并没有不同