+<%
+//ajax片段开始
+#ajax userTable: {
+%>
+
+
+ | id | 姓名 |
+ <% for(user in users){ %>
+ | ${user.id} | ${user.name} |
+ <% } %>
+
+
+当前页面
${page!1}
+
next pre
+<%
+//ajax片段结尾
+}
+%>
+```
+
+\#ajax 用于告诉告诉模板引擎,此处是个局部渲染标记,标记为"userTable",对于正常渲染视图"index.html"页面,#ajax标记没什么用处,table仍能得到正常渲染。如果渲染的视图是index.html#userTable,则模板只会渲染#ajax标记得模板片段,其他部分将忽略。关于完整例子,可以参考http://beetlajax.oschina.mopaas.com/
+
+后台代码如下:
+
+```javascript
+render("/index.html#userTable");
+```
+
+ajax 片段渲染也支持默认情况下不渲染,仅仅做为一个片段使用,如一个页面有许多后台交互操作,并返回相应的html片段,可以将这些html片段也放到同一个模板里,使用ajax norender,表示渲染整个模板的时候默认并不需要渲染此ajax片段
+
+```javascript
+<%
+
+
+
+#ajax norender success: {
+%>
+
操作成功
+
+
+<%
+}
+%>
+
+#ajax norender failure: {
+%>
+
操作失败
+
+
+<%
+}
+%>
+```
+
+这样,此页面默认情况下并没有输出success,和 failure片段
+
+>注意,Ajax片段本质上是从模版的ajax标记处开始渲染,因此,ajax需要的变量在模版里也必须是全局变量,如果你只是个局部变量,beetl会报出找不到变量,即使你binding了这个变量,beetl也认为这个是局部变量,如
+>
+>```javascript
+><%
+>var tableData = paras.table;
+>#ajax userTable: {
+>for(user in tableData);
+> %>
+>
+><%
+>//ajax片段结尾
+>}
+> %>
+>```
+>
+>变量tableData是从paras里获取的,是个临时变量,因此就算你在后台binding了一个tableData,beetl 也不能识别。在渲染ajax片段的时候会报变量tableData找不到。改正的办法只能是让tableData全局变量。
+
+> 返回Json好还是返回html片段好?这个难以定论.
+>
+> - 从后台性能看,将模型序列化成json性能会比渲染模板性能更好,但是,json还需要前端重新解析生成最终html dom节点,这可能会延迟最终数据的现实效果。而返回的html片段就是已经生成好的dom
+> - 从网络传入来看,json无疑更好的,html片段会有额外的html标记,css属性,以及有可能的js调用。传入流量有可能增加50%到100%。但是,对于web应用类,这些额外数据,并不算多。
+> - 从开发效率来讲,返回html片段的开发效率更高一些,因为渲染在后台操作,可以随心所欲的用模板语言来渲染,来取得后台数据,完成复杂渲染,而json就比较困难,可以说所有的json lib都没有完美的解决办法。
+> - 从用户体验上来讲,Beetl 采用ajax标记,混合了传统的模板渲染和ajax加载。用户进入页面即能看到数据,而经典的ajax json方式还需要异步加载,显示延迟。另外如果页面同时有多个ajax加载,则会对服务器造成很大的压力。
+> - 关心服务器cpu消耗? 模板方式消耗更多的cpu,json方式则少点。但是俩者差距并不大。而且更多的web网站面临的情况是有富余的服务器CPU能力
+> - 关心客户端CPU消耗? 过多的js无疑是客户端运行慢的主要原因。如果采用经典的json方式,返回的json数据必然还需要经过js的计算和渲染。会影响客户机器cpu。
+
+
+
+#### 4.13. 在页面输出错误提示信息
+
+2.2.3版本以后,新增加org.beetl.ext.web.WebErrorHandler,可以在web开发的时候在页面输出提示信息,在产品模式下载后台输出提示信息(通过配置属性ESOURCE.autoCheck= true来认为是开发模式),仅仅需要配置如下:
+
+```properties
+ERROR_HANDLER = org.beetl.ext.web.WebErrorHandler
+```
+
+
+
+
+
+### 5. 附录
+
+#### 5.1. 内置方法
+
+##### 5.1.1. 常用内置方法
+
+- **date** 返回一个java.util.Date类型的变量,如 date() 返回一个当前时间(对应java的java.util.Date); ${date( "2011-1-1" , "yyyy-MM-dd" )} 返回指定日期
+- **print** 打印一个对象 print(user.name);
+- **println** 打印一个对象以及回车换行符号,回车换号符号使用的是模板本身的,而不是本地系统的.如果仅仅打印一个换行符,则直接调用println() 即可
+- **nvl** 函数nvl,如果对象为null,则返回第二个参数,否则,返回自己 nvl(user,"不存在")
+- **isEmpty** 判断变量或者表达式是否为空,变量不存在,变量为null,变量是空字符串,变量是空集合,变量是空数组,此函数都将返回true
+- **isNotEmpty** 同上,判断对象是否不为空
+- has 变量名为参数,判断是否存在此全局变量,如 has(userList),类似于1.x版本的exist("userList"),但不需要输入引号了
+- **assert** 如果表达式为false,则抛出异常
+- **trunc** 截取数字,保留指定的小数位,如trunc(12.456,2) 输出是12.45
+- **decode** 一个简化的if else 结构,如 decode(a,1,"a=1",2,"a=2","不知道了")},如果a是1,这decode输出"a=1",如果a是2,则输出"a==2", 如果是其他值,则输出"不知道了"
+- debug 在控制台输出debug指定的对象以及所在模板文件以及模板中的行数,如debug(1),则输出1 [在3行@/org/beetl/core/lab/hello.txt],也可以输出多个,如debug("hi",a),则输出hi,a=123,[在3行@/org/beetl/core/lab/hello.txt]
+- **parseInt** 将数字或者字符解析为整形 如 parseInt("123");
+- **parseLong** 将数字或者字符解析为长整形,parseInt(123.12);
+- **parseDouble** 将数字或者字符解析为浮点类型 如parseDouble("1.23")
+- **range** 接收三个参数,初始值,结束值,还有步增(可以不需要,则默认为1),返回一个Iterator,常用于循环中,如for(var i in range(1,5)) {print(i)},将依次打印1234.
+- **flush** 强制io输出。
+- **json**,将对象转成json字符串,如 var data = json(userList) 可以跟一个序列化规则 如,var data = json(userList,"[*].id:i"),具体参考 [https://git.oschina.net/xiandafu/beetl-json](https://git.oschina.net/xiandafu/beetl-json)
+- **pageCtx** ,仅仅在web开发中,设置一个变量,然后可以在页面渲染过程中,调用此api获取,如pageCtx("title","用户添加页面"),在其后任何地方,可以pageCtx("title") 获取该变量
+- **type.new** 创建一个对象实例,如 var user = type.new("com.xx.User"); 如果配置了IMPORT_PACKAGE,则可以省略包名,type.new("User")
+- **type.name** 返回一个实例的名字,var userClassName = type.name(user),返回"User"
+
+##### 5.1.2. 字符串相关方法
+
+> strutil方法对参数均不做空指针检测,你可自定义方法来覆盖这些内置的方法
+
+- **strutil.startWith** ${ strutil.startWith(“hello”,”he”) 输出是true
+- **strutil.endWith** ${ strutil.endWith(“hello”,”o”) 输出是true
+- **strutil.length** ${ strutil. length (“hello”),输出是5
+- **strutil.subString** ${ strutil.subString (“hello”,1),输出是“ello”
+- strutil.subStringTo ${ strutil.subStringTo (“hello”,1,2),输出是“e”
+- **strutil.split** ${ strutil.split (“hello,joeli”,”,”),输出是数组,有俩个元素,第一个是hello,第二个是joelli”
+- strutil.contain ${ strutil.contain (“hello,”el”),输出是true
+- **strutil.toUpperCase** ${ strutil.toUpperCase (“hello”),输出是HELLO
+- **strutil.toLowerCase** ${ strutil.toLowerCase (“Hello”),输出是hello
+- **strutil.replace** ${ strutil.replace (“Hello”,”lo”,”loooo”),输出是helloooo
+- strutil.format ${ strutil.format (“hello,{0}, my age is {1}”,”joeli”,15),输出是hello,joelli, my age is 15. 具体请参考http://docs.oracle.com/javase/6/docs/api/java/text/MessageFormat.html
+- **strutil.trim** 去掉字符串的尾部空格
+- **strutil.formatDate** var a = strutil.formatDate(user.bir,’yyyy-MM-dd’);
+- **strutil.index** var index = strutil.index("abc","a");返回 索引0
+- **strutil.lastIndex** var index = strutil.lastIndex("aba","a");返回索引2
+
+##### 5.1.3. 数组相关方法
+
+- **array.range** 返回数组或者Collection一部分,接受三个参数,第一个是数组或者Collection子类,第二,三个参数分别是起始位置
+- **array.remove** 删除某个数组或者Collection的一个元素,并返回该数组或者Collection.第一个是数组或者Collection子类,第二个参数是元素
+- **array.add** 向数组或者Collection添加一个元素,并返回该数组或者Collection。第一个是数组或者Collection子类,第二个参数是元素
+- array.contain 判断数组或者元素是否包含元素,如果包含,返回true。否则false。第一个是数组或者Collection子类,第二个参数是元素
+- **array.toArray** 转化成数组,如array.toArray(1,2,"a");
+- **array.collection2Array** 将java集合转化为数组 array.collection2Array([1,2,''])
+
+##### 5.1.4. 正则表达式相关方法
+
+- **reg.match(str,regex)** str为需要处理的字符串,regex是表达式
+- **reg.replace(str,regex,replace)**,str为需要处理的字符串,regex是表达式,替换的字符串替换字符串
+- **reg.find(str,regex)** 返回找到的符合表达式的第一个字符串,否则返回空字符串
+- **reg.findList(str,regex)** 找到所有符合表达式的字符串,否则返回空列表
+- **reg.split(str,regex)**,对字符串进行切分,返回列表
+- **reg.split(str,regex,limit)** 同上,limit是最多返回个数
+
+##### 5.1.5. Spring 相关函数
+
+Spring函数并没有内置,需要注册,如下
+
+```xml
+
+
+
+
+
+
+
+
+```
+
+spel(spelString, rootObject) SpEL方法传入一个Spring SpEL表达式以获取表达式结果,方法建议以函数的方式定义在BeetlGroupUtilConfiguration的functions中
+
+spelString: SpEL表达式字符串,必传(否则返回null) rootObject: 作为spel的根对象(对应#root),可以是一个Map或Bean对象,默认取空Map。由于Beetl运行上下文无法直接获取模版局部变量的变量名,建议局部变量采用自定义Map的方式传入
+
+- 列表筛选(以自定义Map为根对象传入局部变量)
+
+ ```javascript
+ <% var intArray = [12, 1, 2, 3]; %>
+ ${spel('#root.intArray.?[#this>10]', {intArray: intArray})}
+ ```
+
+- 以Bean对象为根对象
+
+ ```javascript
+ <% var now = date(); %>
+ ${spel('#root.year + 1900', now)}
+ ```
+
+- 直接new对象
+
+ ```javascript
+ ${spel('(new java.util.Date()).year + 1900')}
+ ```
+
+- 直接引用Spring Bean
+
+ ```javascript
+ ${spel('@testBean')}
+ ```
+
+- 默认变量
+
+- \#root 表示SpEL的根对象, 由spel函数第二参数传入,默认是一个空map
+
+- \#context 表示Beetl执行上下文
+
+- \#global 表示Beetl的共享变量Map,由于Beetl上下文无法获取临时变量名,临时变量建议使用根对象的方式传入
+
+- \#ctxPath 表示Servlet Context Path(由Beetl WebRender提供)
+
+- \#servlet 可以从中获取到Servlet request,response,session原生实例(由Beetl WebRender提供)
+
+- \#parameter 表示请求参数Map(由Beetl WebRender提供)
+
+- \#request 表示请求对象(由Beetl WebRender提供)
+
+- \#session 表示会话域属性Map(由Beetl WebRender提供)
+
+sputil 提供了spring内置的一些功能,如
+
+```java
+// 测试source中是否包含了candidates的某个成员(相当于交集非空)
+sputil.containsAny(Collection> source, Collection> candidates)
+// 返回在source集合总第一个也属于candidates集的元素
+sputil.findFirstMatch(Collection> source, Collection> candidates)
+// 测试指定文本是否匹配指定的Ant表达式(\*表达式), 多个表达式只要一个匹配即可
+sputil.antMatch(String input, String... patterns)
+// 返回指定路径表示的文件的扩展名(不带点.)
+sputil.fileExtension(String path)
+// 忽略大小写的endsWith
+sputil.endsWithIgnoreCase(String input, String suffix)
+// 忽略大小写的startsWith
+sputil.startsWithIgnoreCase(String input, String prefix)
+// 测试输入值是否为空白, null视为空白, 无视字符串中的空白字符
+sputil.isBlank(String input)
+// 首字母大写转换
+sputil.capitalize(String input)
+// 首字母小写转换
+sputil.uncapitalize(String input)
+// 在集合或数组元素之间拼接指定分隔符返回字符串
+// null表示空集, 其他类型表示单元素集合
+sputil.join(Object collection, String delim)
+// 同上, 只是会在最后结果前后加上前缀和后缀
+// 注意这个函数名叫做joinEx
+sputil.joinEx(Object collection, String delim, String prefix, String suffix)
+// 对文本进行html转义
+sputil.html(String input)
+// 对文本进行javascript转义
+sputil.javaScript(String input)
+```
+
+##### 5.1.6. Spring security
+
+下列三个函数只需以函数的方式定义在BeetlGroupUtilConfiguration的functions中即可,与spel函数一样的,函数名声明在functions中,可以更改
+
+- auth() 对应类: org.beetl.ext.spring.AuthenticationFunction 方法无参数 返回值: 返回当前安全上下文中的用户认证凭证Authentication实例 如果当前环境不存在Spring Security安全上下文,将返回null值
+- urlIf(\
, \) 对应类: org.beetl.ext.spring.AccessUrlIfFunction 参数: url: 字符串表示的测试URL Path,不需要指定Context Path,缺省会直接返回true method: 字符串表示的访问方式, 默认为GET, 建议全大写 返回值: 测试当前登录用户是否能访问指定的URL Path, 返回true or false
+
+示例:
+
+```javascript
+urlIf('/system/admin_update.do', 'POST'))
+```
+
+如果当前环境不存在Spring Security安全上下文,将返回true 如果当前环境不存在用户认证凭证,作为匿名登录进行测试
+
+- expIf(\) 对应类: org.beetl.ext.spring.AccessExpressionIfFunction 参数: exp: Spring Security安全表达式,缺省会直接返回true 返回值: 测试当前登录用户是否满足指定的安全表达式,返回true or false 示例:
+
+ ```javascript
+ expIf('isAuthenticated()')
+ ```
+
+如果当前环境不存在Spring Security安全上下文,将返回true 如果当前环境不存在用户认证凭证,作为匿名登录进行测试
+
+注意: 使用此方法,必须开启Spring Security的expression功能(use-expressions="true"):
+
+```xml
+
+```
+
+Spring Security Expression相关语法,请阅读: [http://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#el-access](http://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#el-access)
+
+##### 5.1.7. shiro
+
+参考文档 [https://my.oschina.net/xiandafu/blog/143109](https://my.oschina.net/xiandafu/blog/143109)
+
+
+
+#### 5.2. 内置格式化方法
+
+- dateFormat 日期格式化函数,如 $*yyyy-Mm-dd*,等于符号后的参数也可以没有,则使用本地默认来做格式化如 ${date,dateFormat}
+- numberFormat ${0.345,numberFormat="#.%"} 输出是 34.5%,具体请参考文档 [http://docs.oracle.com/javase/6/docs/api/java/text/DecimalFormat.html](http://docs.oracle.com/javase/6/docs/api/java/text/DecimalFormat.html)
+
+
+
+#### 5.3. 内置标签函数
+
+- include include一个模板,如 :
+
+ ```javascript
+ <% include("/header.html"){} %>
+ ```
+
+ 如果想往子模板中传入参数,则可以后面跟一个json变量
+
+ ```javascript
+ <% include("/header.html",{'user':user,'id',user.id}){} %>
+ ```
+
+ 这样user,和id 可以在header.html被引用,并成为header.html的全局变量
+
+ > (beetl1.2 也叫includeFileTemplate ,2.0仍然支持,但不再文档里体现了)
+
+- layout 提供一个布局功能,每个页面总是由一定布局,如页面头,菜单,页面脚,以及正文。 layout标签允许为正文指定一个布局,如下使用方式
+
+ content.html内容如下:
+
+ ```javascript
+ <%
+ //content.html内容如下:
+ layout("/inc/layout.html"){ %>
+ this is 正文
+ ..........
+ <% } %>
+ ```
+
+ layout.html 是布局文件,内容如下
+
+ ```javascript
+ <% include("/inc/header.html"){} %>
+ this is content:${layoutContent}
+ this is footer:
+ ```
+
+ 运行content.html模板文件后,,正文文件的内容将被替换到layoutContent的地方,变成如下内容
+
+ ```javascript
+ this is header
+ this is content:this is 正文
+ ............
+ this is footer:
+ ```
+
+ 如果想往layout页面传入参数,则传入一个json变量,如下往layout.html页面传入一个用户登录时间
+
+ ```javascript
+ <% layout("/inc/header.html",{'date':user.loginDate,'title':"内容页面"}){ %>
+ this is 正文
+ ..........
+ <% } %>
+ ```
+
+ 如果layoutContent 命名有冲突,可以在layout第三个参数指定,如
+
+ ```javascript
+ <% layout("/inc/header.html",{'date':user.loginDate,'title':"内容页面"},"myLayoutContent"){ %>
+ this is 正文
+ ..........
+ <% } %>
+ ```
+
+- cache 能Cache标签的内容,并指定多长时间刷新,如
+
+ ```javascript
+ <% :cache('key2',10,false){ %>
+ 内容体
+ <% } %>
+ ```
+
+ 需要指定三个参数
+
+ - 第一个是cache的Key值
+ - 第二个是缓存存在的时间,秒为单位
+ - 第三个表示是否强制刷新,false表示不,true表示强制刷新
+
+ Cache默认实现org.beetl.ext.tag.cache.SimpleCacheManager. 你可以设置你自己的Cache实现,通过调用CacheTag. cacheManager= new YourCacheImplementation();
+
+ 可以在程序里调用如下方法手工删除Cache:
+
+ ```java
+ public void clearAll();
+
+ public void clearAll(String key);
+
+ public void clearAll(String... keys);
+ ```
+
+- includeJSP,可以在模板里包括一个jsp文件,如:
+
+ ```javascript
+ <%
+ includeJSP("/xxxx.jsp",{"key":"value"}){}
+ %>
+ ```
+
+ key value 都是字符串,将以parameter的形式提供给jsp,因此jsp可以通过request.getParameter("key")来获取参数
+
+ 主要注意的是,这个标签并非内置,需要手工注册一下
+
+ ```java
+ groupTemplate.registerTag("incdlueJSP",org.beetl.ext.jsp.IncludeJSPTag.class);
+ ```
+
+
+
+#### 5.4. 性能优化的秘密
+
+Beetl2.0目前只完成了解释引擎,使用解释引擎好处是可以适用于各种场景,性能测试表明,Beetl2.0引擎是Freemaker的4-6倍,跟最好的 的编译引擎性能相比,也相差只有30%百分点。为什么Beetl能跑的如此之快呢,简单的说,有如下策略
+
+- 优化IO输出,允许使用字节直接输出,模板中的静态文本事先转化为字节
+- encode优化,对于number类型,输出通常是.toString 转化成String,然后encode输出,这中间浪费了大量的资源,Beetl实现了encode,输出一步到位
+- Context 采用一维数组,语言里的Context通常采用Map实现,每次进入{} ,就新增一个child Map,尽管map很快,但不够快。也有其他模板语言采用二位数组提高性能,Beetl是通过固定大小一维数组来维护模板的Context,因此访问更快,也避免了Map和二维素组的频繁创建。其实除了此处,beetl很多地方都不采用Map来维护key-value, 而都采用数组索引,以追求性能极限
+- 字节码访问属性,通过反射获取性能比较慢,就算JVM有优化,但优化效果也不确定。Beetl通过字节码生成了属性访问类,从而将属性访问速度提高了一个数量级
+- 类型推测:Beetl 是强制类型的,因此预先知道类型,可以对模板做一些优化而省去了动态判断类型的时间
+- 使用数组Buffer,避免频繁创建和销毁数组
+- 编译引擎将模板编译成类,会产生大量的类,虚拟机很难对这些做优化。而解释引擎只有几十个固定的类,虚拟机容易优化
+
+> #### 相关文章
+>
+> - 为什么JSP会比Beetl慢 [http://my.oschina.net/xiandafu/blog/475740](http://my.oschina.net/xiandafu/blog/475740)
+> - Beetl 性能揭秘 2 :语言如何存取变量 [http://my.oschina.net/xiandafu/blog/293167](http://my.oschina.net/xiandafu/blog/293167)
+> - Beetl 性能揭秘 1 :如何输出一个整型变量 [http://my.oschina.net/xiandafu/blog/284823](http://my.oschina.net/xiandafu/blog/284823)
+
+
+
+#### 5.5. 性能测试对比
+
+测试用例一 [https://github.com/javamonkey/ebm](https://github.com/javamonkey/ebm)
+
+
+
+测试用例二 [http://git.oschina.net/kiang/teb](http://git.oschina.net/kiang/teb)
+
+
+
+测试用例三 https://github.com/javamonkey/template-benchmark
+
+
+
+| Benchmark | version | Threads | Samples | Score | Score Error (99.9%) | Unit |
+| -------------- | ------- | ------- | ------- | ---------------- | ------------------- | ----- |
+| **Beetl** | 2.7 | 1 | 50 | **42125.112914** | 3512.147131 | ops/s |
+| **Freemarker** | 2.3 | 1 | 50 | **13099.139808** | 339.612022 | ops/s |
+| **Handlebars** | 4.0 | 1 | 50 | **15808.044125** | 235.109622 | ops/s |
+| **Mustache** | 0.9 | 1 | 50 | **17961.391809** | 158.524109 | ops/s |
+| **Rocker** | 0.1 | 1 | 50 | **33631.370722** | 417.915637 | ops/s |
+| **Thymeleaf** | 3.0 | 1 | 50 | **4625.981276** | 67.313609 | ops/s |
+
+> #### 注意
+>
+> Score得分越高表示模板引擎每秒处理量越大性能越好
+>
+> 这个性能测试基本上结合了国内外的模板引擎,随着JDK版本的升级,JDK8提高了反射能力,减少了和Freemarker等模板引擎的性能差距,但Beetl依旧以3倍以上的性能优势秒杀Freemarker。
+
+#### 5.6. Beetl 开发团队
+
+作者
+
+- 闲.大赋
+
+助手
+
+- 作死模式:核心代码开发
+- 一粟蜉蝣:核心代码开发和版本发布
+
+代码捐助者
+
+- 逝水fox :出色完成spring集成
+- kraken: 集合方法等扩展
+- 西安玛雅牛:复合加载器
+- 级?!: beetl扩展,crossMVC
+- orangetys: beetl插件
+- Oo不懂oO: beetl插件
+- 原上一颗草:Beetl早期使用者。
+- 龙图腾飞 ,WebErrorHandler,用来开发模式在 web上显示错误而不是控制台
+- nutz: nutz 集成和MapResourceLoader
+- 天方地圆 :提供正则方法
+
+文档校验
+
+- 九月
+- Daemons
\ No newline at end of file
diff --git a/doc/guide/beetlsql.md b/doc/guide/beetlsql.md
new file mode 100644
index 00000000..a877e4dc
--- /dev/null
+++ b/doc/guide/beetlsql.md
@@ -0,0 +1,1929 @@
+## BeetlSQL中文文档
+
+> - 作者: 闲大赋,Gavin.King,Sue,Zhoupan,woate
+> - 社区 [http://ibeetl.com](http://ibeetl.com/)
+> - qq群 219324263
+> - 当前版本 2.7.3 , 另外还需要beetl([http://git.oschina.net/xiandafu/beetl2.0/attach_files](http://git.oschina.net/xiandafu/beetl2.0/attach_files)) 包
+
+
+
+### 1. BeetlSQL 特点
+
+BeetSql是一个全功能DAO工具, 同时具有Hibernate 优点 & Mybatis优点功能,适用于承认以SQL为中心,同时又需求工具能自动能生成大量常用的SQL的应用。
+
+- 开发效率
+ - 无需注解,自动使用大量内置SQL,轻易完成增删改查功能,节省50%的开发工作量
+ - 数据模型支持Pojo,也支持Map/List这种快速模型,也支持混合模型
+ - SQL 模板基于Beetl实现,更容易写和调试,以及扩展
+ - 可以针对单个表(或者视图)代码生成pojo类和sql模版,甚至是整个数据库。能减少代码编写工作量
+- 维护性
+ - SQL 以更简洁的方式,Markdown方式集中管理,同时方便程序开发和数据库SQL调试。
+ - 可以自动将sql文件映射为dao接口类
+ - 灵活直观的支持支持一对一,一对多,多对多关系映射而不引入复杂的OR Mapping概念和技术。
+ - 具备Interceptor功能,可以调试,性能诊断SQL,以及扩展其他功能
+- 其他
+ - 内置支持主从数据库支持的开源工具
+ - 支持跨数据库平台,开发者所需工作减少到最小,目前跨数据库支持mysql,postgres,oracle,sqlserver,h2,sqllite.
+
+
+
+### 2. 5分钟例子
+
+#### 2.1. 安装
+
+maven 方式:
+
+```xml
+
+ com.ibeetl
+ beetlsql
+ 2.7.3
+
+```
+
+或者依次下载beetlsql,beetl 最新版本 包放到classpath里
+
+
+
+#### 2.2. 准备工作
+
+为了快速尝试BeetlSQL,需要准备一个Mysql数据库或者其他任何beetlsql支持的数据库,然后执行如下sql脚本
+
+```sql
+CREATE TABLE `user` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `name` varchar(64) DEFAULT NULL,
+ `age` int(4) DEFAULT NULL,
+ `userName` varchar(64) DEFAULT NULL COMMENT '用户名称',
+ `roleId` int(11) DEFAULT NULL COMMENT '用户角色',
+ `date` datetime NULL DEFAULT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+```
+
+编写一个Pojo类,与数据库表对应(或者可以通过SQLManager的gen方法生成此类,参考一下节)
+
+```java
+import java.math.*;
+import java.util.Date;
+
+/*
+*
+* gen by beetlsql 2016-01-06
+*/
+public class User {
+ private Integer id ;
+ private Integer age ;
+ //用户角色
+ private Integer roleId ;
+ private String name ;
+ //用户名称
+ private String userName ;
+ private Date date ;
+
+}
+```
+
+
+
+#### 2.3. 代码例子
+
+写一个java的Main方法,内容如下
+
+```java
+ConnectionSource source = ConnectionSourceHelper.getSimple(driver, url, "", userName, password);
+DBStyle mysql = new MySqlStyle();
+// sql语句放在classpagth的/sql 目录下
+SQLLoader loader = new ClasspathLoader("/sql");
+// 数据库命名跟java命名一样,所以采用DefaultNameConversion,还有一个是UnderlinedNameConversion,下划线风格的
+所以采用DefaultNameConversion nc = new 所以采用DefaultNameConversion();
+// 最后,创建一个SQLManager,DebugInterceptor 不是必须的,但可以通过它查看sql执行情况
+SQLManager sqlManager = new SQLManager(mysql,loader,source,nc,new Interceptor[]{new DebugInterceptor()});
+
+
+//使用内置的生成的sql 新增用户,如果需要获取主键,可以传入KeyHolder
+User user = new User();
+user.setAge(19);
+user.setName("xiandafu");
+sqlManager.insert(user);
+
+//使用内置sql查询用户
+int id = 1;
+user = sqlManager.unique(User.class,id);
+
+//模板更新,仅仅根据id更新值不为null的列
+User newUser = new User();
+newUser.setId(1);
+newUser.setAge(20);
+sqlManager.updateTemplateById(newUser);
+
+//模板查询
+User query = new User();
+query.setName("xiandafu");
+List list = sqlManager.template(query);
+
+
+//使用user.md 文件里的select语句,参考下一节。
+User query2 = new User();
+query.setName("xiandafu");
+List list2 = sqlManager.select("user.select",User.class,query2);
+```
+
+
+
+#### 2.4. SQL文件例子
+
+通常一个项目还是有少量复杂sql,可能只有5,6行,也可能有上百行,放在单独的sql文件里更容易编写和维护,为了能执行上例的user.select,需要在classpath里建立一个sql目录(ClasspathLoader 配置成sql目录,参考上一节ClasspathLoader初始化的代码)以及下面的user.md 文件,内容如下
+
+```markdown
+select
+===
+select * from user where 1=1
+@if(!isEmpty(age)){
+and age = #age#
+@}
+@if(!isEmpty(name)){
+and name = #name#
+@}
+```
+
+关于如何写sql模板,会稍后章节说明,如下是一些简单说明。
+
+- 采用md格式,===上面是sql语句在本文件里的唯一标示,下面则是sql语句。
+- @ 和回车符号是定界符号,可以在里面写beetl语句。
+- "#" 是站位符号,生成sql语句得时候,将输出?,如果你想输出表达式值,需要用text函数,或者任何以db开头的函数,引擎则认为是直接输出文本。
+- isEmpty是beetl的一个函数,用来判断变量是否为空或者是否不存在.
+- 文件名约定为类名,首字母小写。
+
+sql模板采用beetl原因是因为beetl 语法类似js,且对模板渲染做了特定优化,相比于mybatis,更加容易掌握和功能强大,可读性更好,也容易在java和数据库之间迁移sql语句
+
+
+
+#### 2.5. 代码&sql生成
+
+User类并非需要自己写,好的实践是可以在项目中专门写个类用来辅助生成pojo和sql片段,代码如下
+
+```java
+public static void main(String[] args){
+ SqlManager sqlManager = ...... //同上面的例子
+ sqlManager.genPojoCodeToConsole("user");
+ sqlManager.genSQLTemplateToConsole("user");
+}
+```
+
+> 注意:我经常在我的项目里写一个这样的辅助类,用来根据表或者视图生成各种代码和sql片段,以快速开发.
+
+genPojoCodeToConsole 方法可以根据数据库表生成相应的Pojo代码,输出到控制台,开发者可以根据这些代码创建相应的类,如上例子,控制台将输出
+
+```java
+package com.test;
+import java.math.*;
+import java.util.Date;
+import java.sql.Timestamp;
+
+/*
+*
+* gen by beetlsql 2016-01-06
+*/
+public class user {
+ private Integer id ;
+ private Integer age ;
+ //用户角色
+ private Integer roleId ;
+ private String name ;
+ //用户名称
+ private String userName ;
+ private Date date ;
+
+}
+```
+
+上述生成的代码有些瑕疵,比如包名总是com.test,类名是小写开头(因为用了DefaultNameConversion),你需要修改成你要的包名和正常的类名,pojo类也没有生成getter,setter方法,你需要用ide自带的工具再次生成一下。
+
+> #### 注意
+>
+> 生成属性的时候,id总是在前面,后面依次是类型为Integer的类型,最后面是日期类型,剩下的按照字母排序放到中间。
+
+一旦有了User 类,如果你需要些sql语句,那么genSQLTemplateToConsole 将是个很好的辅助方法,可以输出一系列sql语句片段,你同样可以赋值粘贴到代码或者sql模板文件里(user.md),如上例所述,当调用genSQLTemplateToConsole的时候,生成如下
+
+```markdown
+sample
+===
+* 注释
+
+ select #use("cols")# from user where #use("condition")#
+
+cols
+===
+
+ id,name,age,userName,roleId,date
+
+updateSample
+===
+
+ `id`=#id#,`name`=#name#,`age`=#age#,`userName`=#userName#,`roleId`=#roleId#,`date`=#date#
+
+condition
+===
+
+ 1 = 1
+ @if(!isEmpty(name)){
+ and `name`=#name#
+ @}
+ @if(!isEmpty(age)){
+ and `age`=#age#
+ @}
+```
+
+beetlsql生成了用于查询,更新,条件的sql片段和一个简单例子。你可以按照你的需要copy到sql模板文件里.实际上,如果你熟悉gen方法,你可以直接gen代码和sql到你的工程里,甚至是整个数据库都可以调用genAll来一次生成
+
+> #### 注意
+>
+> sql 片段的生成顺序按照数据库表定义的顺序显示
+
+
+
+### 3. BeetlSQL 说明
+
+#### 3.1. 获得SQLManager
+
+SQLManager 是系统的核心,他提供了所有的dao方法。获得SQLManager,可以直接构造SQLManager.并通过过单例获取如:
+
+```java
+ConnectionSource source = ConnectionSourceHelper.getSimple(driver, url, "", userName, password);
+DBStyle mysql = new MySqlStyle();
+// sql语句放在classpagth的/sql 目录下
+SQLLoader loader = new ClasspathLoader("/sql");
+// 数据库命名跟java命名一样,所以采用DefaultNameConversion,还有一个是UnderlinedNameConversion,下划线风格的
+UnderlinedNameConversion nc = new UnderlinedNameConversion();
+// 最后,创建一个SQLManager,DebugInterceptor 不是必须的,但可以通过它查看sql执行情况
+SQLManager sqlManager = new SQLManager(mysql,loader,source,nc,new Interceptor[]{new DebugInterceptor()});
+```
+
+更常见的是,已经有了DataSource,创建ConnectionSource 可以采用如下代码
+
+```java
+ConnectionSource source = ConnectionSourceHelper.single(datasource);
+```
+
+如果是主从Datasource
+
+```java
+ConnectionSource source = ConnectionSourceHelper.getMasterSlave(master,slaves)
+```
+
+
+
+#### 3.2. 查询API
+
+##### 3.2.1. 模板类查询(自动生成sql)
+
+- public List all(Class clazz) 查询出所有结果集
+- public List all(Class clazz, int start, int size) 翻页
+- public int allCount(Class> clazz) 总数
+- public List template(T t) 根据模板查询,返回所有符合这个模板的数据库 同上,mapper可以提供额外的映射,如处理一对多,一对一
+- public List template(T t,int start,int size) 同上,可以翻页
+- public long templateCount(T t) 获取符合条件的个数
+
+翻页的start,系统默认位从1开始,为了兼容各个数据库系统,会自动翻译成数据库习俗,比如start为1,会认为mysql,postgres从0开始(从start-1开始),oralce从1开始(start-0)开始。
+
+然而,如果你只用特定数据库,可以按照特定数据库习俗来,比如,你只用mysql,start为0代表起始纪录,需要配置
+
+```properties
+OFFSET_START_ZERO = true
+```
+
+这样,翻页参数start传入0即可。
+
+注意:根据模板查询并不包含时间字段,也不包含排序,然而,可以通过在pojo class上使用@TableTemplate() 或者日期字段的getter方法上使用@DateTemplate()来定制,如下:
+
+```java
+@TableTemplate("order by id desc ")
+public class User {
+ private Integer id ;
+ private Integer age ;
+ // ...
+ @DateTemplate(accept="minDate,maxDate")
+ public Date getDate() {
+ return date;
+ }
+}
+```
+
+这样,模板查询将添加order by id desc ,以及date字段将按照日期范围来查询。 具体参考annotation一章
+
+##### 3.2.2. 通过sqlid查询,sql语句在md文件里
+
+- public List select(String sqlId, Class clazz, Map paras) 根据sqlid来查询,参数是个map
+- public List select(String sqlId, Class clazz, Object paras) 根据sqlid来查询,参数是个pojo
+- public List select(String sqlId, Class clazz, Map paras, int start, int size), 增加翻页
+- public List select(String sqlId, Class clazz, Object paras, int start, int size) ,增加翻页
+- public T selectSingle(String id,Object paras, Class target) 根据sqlid查询,输入是Pojo,将对应的唯一值映射成指定的taget对象,如果未找到,则返回空。需要注意的时候,有时候结果集本生是空,这时候建议使用unique
+- public T selectSingle(String id,Map paras, Class target) 根据sqlid查询,输入是Map,将对应的唯一值映射成指定的taget对象,如果未找到,则返回空。需要注意的时候,有时候结果集本生是空,这时候建议使用unique
+- public T selectUnique(String id,Object paras, Class target) 根据sqlid查询,输入是Pojo或者Map,将对应的唯一值映射成指定的taget对象,如果未找到,则抛出异常
+- public T selectUnique(String id,Map paras, Class target) 根据sqlid查询,输入是Pojo或者Map,将对应的唯一值映射成指定的taget对象,如果未找到,则抛出异常
+- public Integer intValue(String id,Object paras) 查询结果映射成Integer,如果找不到,返回null,输入是object
+- public Integer intValue(String id,Map paras) 查询结果映射成Integer,如果找不到,返回null,输入是map,其他还有 longValue,bigDecimalValue
+- public T unique(Class clazz,Object pk) 根据主键查询,如果未找到,抛出异常.
+
+
+
+
+#### 3.3. 翻页查询API
+
+- public void pageQuery(String sqlId,Class clazz,PageQuery query)
+
+BeetlSQL 提供一个PageQUery对象,用于web应用的翻页查询,BeetlSql假定有sqlId 和sqlId$count,俩个sqlId,并用这来个来翻页和查询结果总数.如:
+
+```markdown
+queryNewUser
+===
+select * from user order by id desc ;
+
+queryNewUser$count
+===
+select count(1) from user
+```
+
+对于俩个相似的sql语句,你可以使用use函数,把公共部分提炼出来.
+
+大部分情况下,都不需要2个sql来完成,一个sql也可以,要求使用page函数或者pageTag标签,这样才能同时获得查询结果集总数和当前查询的结果
+
+```markdown
+queryNewUser
+===
+select
+@pageTag(){
+a.*,b.name role_name
+@}
+from user a left join b ...
+```
+
+如上sql,会在查询的时候转为俩条sql语句
+
+```sql
+select count(1) from user a left join b...
+select a.*,b.name role_name from user a left join b...
+```
+
+如果字段较多,为了输出方便,也可以使用pageTag,字段较少,用page函数也可以. ,具体参考pageTag和page函数说明.翻页代码如下
+
+```java
+//从第一页开始查询,无参数
+PageQuery query = new PageQuery();
+sql.pageQuery("user.queryNewUser", User.class,query);
+System.out.println(query.getTotalPage());
+System.out.println(query.getTotalRow());
+System.out.println(query.getPageNumber());
+List list = query.getList();
+```
+
+PageQuery 对象也提供了 orderBy属性,用于数据库排序,如 "id desc"
+
+> #### 跨数据库支持
+>
+> 如果你打算使用PageQuery做翻页,且只想提供一个sql语句+page函数,那考虑到垮数据库,应该不要在这个sql语句里包含排序,因为大部分数据库都不支持. page函数生成的查询总数sql语句,因为包含了oder by,在大部分数据库都是会报错的的,比如:select count(1) form user order by name,在sqlserver,mysql,postgres都会出错,oracle允许这种情况, 因此,如果你要使用一条sql语句+page函数,建议排序用PageQuery对象里有排序属性oderBy,可用于排序,而不是放在sql语句里.
+>
+> 如果你不打算使用PageQuery+一条sql的方式,而是用俩条sql来分别翻页查询和统计总数,那无所谓
+>
+> 或者你直接使用select 带有起始和读取总数的接口,也没有关系,可以在sql语句里包含排序
+
+
+
+#### 3.4. 更新API
+
+##### 3.4.1. 自动生成sql
+
+- public void insert(Class> clazz,Object paras) 插入paras到paras关联的表
+- public void insert(Class> clazz,Object paras,KeyHolder holder),插入paras到paras关联的表,如果需要主键,可以通过holder的getKey来获取
+- public int insert(Class clazz,Object paras,boolean autoAssignKey) 插入paras,并且指定是否自动将数据库主键赋值到paras里
+- public int updateById(Object obj) 根据主键更新,主键通过annotation表示,如果没有,则认为属性id是主键,所有值参与更新
+- public int updateTemplateById(Object obj) 根据主键更新,组件通过annotation表示,如果没有,则认为属性id是主键,属性为null的不会更新
+- public int updateTemplateById(Class> clazz,Map paras) 根据主键更新,组件通过clazz的annotation表示,如果没有,则认为属性id是主键,属性为null的不会更新。
+- public int[] updateByIdBatch(List> list) 批量更新
+- public void insertBatch(Class clazz,List> list) 批量插入数据
+
+##### 3.4.2. 通过sqlid更新
+
+- public int insert(String sqlId,Object paras,KeyHolder holder) 根据sqlId 插入,并返回主键,主键id由paras对象所指定
+- public int insert(String sqlId,Object paras,KeyHolder holder,String keyName) 同上,主键由keyName指定
+- public int insert(String sqlId,Map paras,KeyHolder holder,String keyName),同上,参数通过map提供
+- public int update(String sqlId, Object obj) 根据sqlid更新
+- public int update(String sqlId, Map paras) 根据sqlid更新,输出参数是map
+- public int[] updateBatch(String sqlId,List> list) 批量更新
+- public int[] updateBatch(String sqlId,Map[] maps) 批量更新,参数是个数组,元素类型是map
+
+
+
+
+#### 3.5. 直接执行SQL
+
+##### 3.5.1. 直接执行sql模板语句
+
+- public List execute(String sql,Class clazz, Object paras)
+- public List execute(String sql,Class clazz, Map paras)
+- public int executeUpdate(String sql,Object paras) 返回成功执行条数
+- public int executeUpdate(String sql,Map paras) 返回成功执行条数
+
+#### 3.5.2. 直接执行JDBC sql语句
+
+- 查询 public List execute(SQLReady p,Class clazz) SQLReady包含了需要执行的sql语句和参数,clazz是查询结果,如
+
+```java
+List list = sqlManager.execute(new SQLReady("select * from user where name=? and age = ?","xiandafu",18),User.class);)
+```
+
+- 更新 public int executeUpdate(SQLReady p) SQLReady包含了需要执行的sql语句和参数,返回更新结果
+
+- 直接使用Connection public T executeOnConnection(OnConnection call),使用者需要实现onConnection方法的call方法,如调用存储过程
+
+```java
+String md5 = sql.executeOnConnection(new OnConnection(){
+ @Override
+ public String call(Connection conn) throws SQLException {
+ CallableStatement cstmt = conn.prepareCall("{ ? = call md5( ? ) }");
+ // 其他代码
+ return true;
+ }
+});
+```
+
+
+
+#### 3.6. 其他
+
+##### 3.6.1. 强制使用主或者从
+
+- public void useMaster(DBRunner f) DBRunner里的beetlsql调用将使用主数据库库
+- public void useSlave(DBRunner f) DBRunner里的beetlsql调用将使用从数据库库
+
+##### 3.6.2. 生成Pojo代码和SQ片段
+
+用于开发阶段根据表名来生成pojo代码和相应的sql文件
+
+- genPojoCodeToConsole(String table), 根据表名生成pojo类,输出到控制台.
+- genSQLTemplateToConsole(String table),生成查询,条件,更新sql模板,输出到控制台。
+- genPojoCode(String table,String pkg,String srcPath,GenConfig config) 根据表名,包名,生成路径,还有配置,生成pojo代码
+- genPojoCode(String table,String pkg,GenConfig config) 同上,生成路径自动是项目src路径,或者src/main/java (如果是maven工程)
+- genPojoCode(String table,String pkg),同上,采用默认的生成配置
+- genSQLFile(String table), 同上,但输出到工程,成为一个sql模版,sql模版文件的位置在src目录下,或者src/main/resources(如果是maven)工程.
+- genALL(String pkg,GenConfig config,GenFilter filter) 生成所有的pojo代码和sql模版,
+- genBuiltInSqlToConsole(Class z) 根据类来生成内置的增删改查sql语句,并打印到控制台
+
+```java
+sql.genAll("com.test", new GenConfig(), new GenFilter(){
+ public boolean accept(String tableName){
+ if(tableName.equalsIgnoreCase("user")){
+ return true;
+ }else{
+ return false;
+ }
+ // return false
+ }
+});
+```
+
+第一个参数是pojo类包名,GenConfig是生成pojo的配置,GenFilter 是过滤,返回true的才会生成。如果GenFilter为null,则数据库所有表都要生成
+
+> #### 警告
+>
+> 必须当心覆盖你掉你原来写好的类和方法,不要轻易使用genAll,如果你用了,最好立刻将其注释掉,或者在genFilter写一些逻辑保证不会生成所有的代码好sql模板文件
+
+
+
+### 4. 命名转化,表和列名映射
+
+Beetlsql 默认提供了三种列明和属性名的映射类。
+
+- DefaultNameConversion 数据库名和java属性名保持一致,如数据库表User,对应Java类也是User,数据库列是sysytemId,则java属性也是systemId,反之亦然
+- UnderlinedNameConversion 将数据库下划线去掉,首字母大写,如数据库是SYS_USER(oralce数据库的表和属性总是大写的), 则会改成SysUser
+- JPANameConversion 支持JPA方式的映射,适合不能用确定的映射关系
+- 自定义命名转化,如果以上3个都不合适,可以自己实现一个命名转化。实现DefaultNameConversion实现方式
+- 因为数据库表和列都忽略大小写区别,所以,实现NameConversion也不需要考虑大小写
+
+```java
+public class DefaultNameConversion extends NameConversion {
+
+ @Override
+ public String getTableName(Class> c) {
+ Table table = (Table)c.getAnnotation(Table.class);
+ if(table!=null){
+ return table.name();
+ }
+ return c.getSimpleName();
+ }
+
+ @Override
+ public String getColName(Class> c, String attrName) {
+ return attrName;
+ }
+
+ @Override
+ public String getPropertyName(Class> c, String colName) {
+ return colName;
+ }
+
+}
+```
+
+如果有特殊的表并不符合DefaultNameConversion实现方式,你可以重新实现上面的三个方法
+
+
+
+### 5. 复合主键
+
+beetlsql 支持复合主键,无需像其他dao工具那样创建一个特别的主键对象,主键对象就是实体对象本生
+
+```sql
+CREATE TABLE `party` (
+ `id1` int(11) NOT NULL,
+ `id2` int(11) NOT NULL,
+ `name` varchar(45) DEFAULT NULL,
+ PRIMARY KEY (`id1`,`id2`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+```
+
+Party代码如下
+
+```java
+public class Party {
+ private Integer id1 ;
+ private Integer id2 ;
+ private String name ;
+ //忽略其他 getter setter方法
+}
+```
+
+根据主键获取Party
+
+```java
+Party key = new Party();
+key.setId1(1);
+key.setId2(2);
+Party party = sql.unique(Party.class, key);
+```
+
+
+
+### 6. 使用Mapper
+
+SQLManager 提供了所有需要知道的API,但通过sqlid来访问sql有时候还是很麻烦,因为需要手敲字符串,另外参数不是map就是para,对代码理解没有好处,BeetlSql支持Mapper,将sql文件映射到一个interface。如下示例
+
+```java
+public interface UserDao extends BaseMapper {
+ // 使用"user.getCount"语句,无参数
+ public int getCount();
+ //使用"user.setUserStatus" 语句
+ public void setUserStatus(Map paras); //更新用户状态
+ public void setUserAnnotherStatus(User user); //更新用户状态
+ //使用"user.findById", 传入参数id
+ public User findById(@Param("id") Integer id);
+ //or 使用params,一一对应
+ @SqlStatement(params="id,status")
+ public User findByIdAndStatus( Integer id,Integer status);
+ //翻页查询,使用"user.queryNewUser"
+ public void queryNewUser(PageQuery query) ;
+ // 使用_st,_sz 翻页
+ @SqlStatement(params="name,age,_st,_sz")
+ public List queryUser( String name, Integer age,int start, int size);
+ //使用sqlready
+ @Sql(value=" update user set age = ? where id = ? ")
+ public void updateAge(int age,int id);
+
+ @Sql(value=" select name from user",returnType=String.class)
+ public List allNames();
+
+}
+```
+
+- Interface 可以继承BaseMapper,这样可以使用BaseMapper的一些公共方法,如insert,unqiue,single,updateById,deleteById等,也可以不继承
+
+- Interface里的方法名与Sql文件对应,如果方法名对应错了,会在调用的时候报错找不到sql。
+
+- 方法参数可以是一个Object,或者是Map,这样,BeetlSql 自动识别为 sql的参数,也可以使用注解@Param来标注,或者混合这俩种情况 如:
+
+```java
+public void setUserStatus(Map paras,@Param("name") String name);
+}
+```
+
+方法如果是查询语句,可以使用@RowStart,@RowSize 作为翻页参数,BeetlSQL将自动完成翻页功能
+
+注意 BeetlSQL 会根据 对应的方法对应的SQL语句,解析开头,如果是select开头,就认为是select操作,同理还有update,delete,insert。如果sql 模板不是以这些关键字开头,则需要使用注解 @SqlStatement
+
+```java
+@SqlStatement(type=SqlStatementType.INSERT)
+public KeyHolder newUser(User user);// 添加用户
+```
+
+SqlStatement 也可在params申明参数名称
+
+```java
+public List queryUser(@Param("name") String name,@Param("age") Integer age,@RowStart int start,@RowSize int size);
+// or
+@SqlStatement(params="name,age,_st,_se")
+public List queryUser(String name,Integer age,int start,int size);
+```
+
+- 查询语句返回的是List,则对应SQLManager.select
+- 查询语句返回的是Pojo,原始类型等非List类型,则对应的SQLManager.selectSignle,如上面的getCount
+- insert 语句 如果有KeyHolder,则表示需要获取主键,对应SQLManager.insert(….,keyHolder)方法
+- 参数列表里只允许有一个Pojo或者Map,作为查询参数_root,否则,需要加上@Param
+- 参数列表里如果有List 或者Map[],则期望对应的是一个updateBatch操作
+- 参数列表里如果@RowStart ,@RowSize,则认为是翻页语句
+- 参数里如果有PageQuery,则认为是翻页查询
+- 在查询中,返回的是List,但类型非Mapper指定的类型,无论是用@Sql ,还是 @SqlStatement,需要用returnType来说明,如上例子allNames 返回一个List而不是List,因此需要使用returnType做额外说明
+
+Mapper 也支持使用JDBC SQL,这时候需要采用Sql注解
+
+```java
+@Sql(value=" update user set age = ? where id = ? ")
+public void updateAge(int age,int id);
+@Sql("select * from user ")
+public List selectAll();
+```
+
+使用Mapper能增加Dao维护性,并能提高开发效率,建议在项目中使用。
+
+
+
+### 7. BeetlSQL Annotation
+
+对于自动生成的sql,默认不需要任何annotaton,类名对应于表名(通过NameConverstion类),getter方法的属性名对应于列明(也是通过NameConverstion类),但有些情况还是需要anntation。
+
+#### 7.1. @AutoID 和 @AssignID ,@SeqID
+
+- @AutoID,作用于getter方法,告诉beetlsql,这是自增主键,对应于数据自增长
+- @AssignID,作用于getter方法,告诉beetlsql,这是程序设定
+
+```java
+@AssignID()
+public Long getId() {
+ return id;
+}
+```
+
+代码设定主键允许像@AssignID 传入id的生成策略以自动生成序列,beetl默认提供了一个snowflake算法,一个用于分布式环境的id生成器([https://github.com/twitter/snowflake](https://github.com/twitter/snowflake))
+
+```java
+@AssignID("simple")
+public Long getId() {
+ return id;
+}
+```
+
+simple 是beetlsql提供的一个默认的snowflake实现,你可以通过sqlManager自己注册id生成器
+
+```java
+sqlManager.addIdAutonGen("uuid2", new IDAutoGen(){
+ @Override
+ public Object nextID(String params) {
+ return "hi"+new Random().nextInt(10000);
+ }
+});
+```
+
+```java
+@AssignID("uuid2")
+public Long getId() {
+ return id;
+}
+```
+
+- @SeqID(name="xx_seq",作用于getter方法,告诉beetlsql,这是序列主键。
+
+对于属性名为id的自增主键,不需要加annotation,beetlsql默认就是@AutoID
+
+> #### 备注
+>
+> - 对于支持多种数据库的,这些annotation可以叠加在一起
+
+
+
+#### 7.2. @Tail
+
+@Tail作用于类上,表示该对象是混合模型,参考下一章混合模型,sql查询无法在pojo映射的列或者结果集将使用Tail指定的方法
+
+
+
+#### 7.3. @ColumnIgnore
+
+在beetlsql 内置的insert或者update方法的时候,使用此注解的字段(作用于getter方法)将根据注解的属性来决定是否忽略此字段
+
+```java
+@ColumnIgnore(insert=true,update=false)
+public Date getBir(){
+ return bir;
+}
+```
+
+如上例子,插入的时候忽略bir属性(往往是因为数据库指定了默认值为当前时间),更新的时候不能忽略 @ColumnIgnore的insert默认是true,update是false,因此也可以直接用 @ColumnIgnore()
+
+
+
+#### 7.4. @EnumMapping
+
+对于Entity使用了枚举作为属性值,可以再枚举类上定义EnumMapping,指出如何将枚举与数据库值互相转化,有四种方法
+
+- 如果没有使用@EnumMapping,则使用枚举的名称作为属性
+- @EnumMapping(EnumMapping.EnumType.STRING) 同上,使用枚举名称作为属性,数据库对应列应该是字符列
+- @EnumMapping(EnumMapping.EnumType.ORDINAL) 使用枚举的顺序作为属性,数据库对应列应该是int类型,用此作为映射需要防止重构枚举的时候导致数据库也重构,应该慎用
+- @EnumMapping(“xxx”),如果不是上面的的定义,则beetlsql会查找枚举类的xxx属性,用这个值作为属性,比如
+
+```java
+@EnumMapping("value")
+public enum Color {
+ RED("RED",1),BLUE ("BLUE",2);
+ private String name;
+ private int value;
+ private Color(String name, int value) {
+ this.name = name;
+ this.value = value;
+ }
+ public String getName() {
+ return name;
+ }
+ public void setName(String name) {
+ this.name = name;
+ }
+ public int getValue() {
+ return value;
+ }
+ public void setValue(int value) {
+ this.value = value;
+ }
+
+}
+```
+
+beetlsq 会获取枚举的value属性(调用getValue)来获取枚举属性值
+
+
+
+#### 7.5. @Table
+
+标签 @Table(name="xxxx") 告诉beetlsql,此类对应xxxx表。比如数据库有User表,User类对应于User表,也可以创建一个UserQuery对象,也对应于User表
+
+```java
+@Table(name="user")
+public class QueryUser ..
+```
+
+注:可以为对象指定一个数据库shcema,如name="cms.user",此时将访问cms库(或者cms用户,对不同的数据库,称谓不一样)下的user数据表
+
+
+
+#### 7.6. @TableTemplate
+
+- @TableTemplate() 用于模板查询,如果没有任何值,将按照主键降序排,也就是order by 主键名称 desc
+- @DateTemplate(),作用于日期字段的getter方法上,有俩个属性accept 和 compare 方法,分别表示 模板查询中,日期字段如果不为空,所在的日期范围,如
+
+```java
+@DateTemplate(accept="minDate,maxDate",compare=">=,<")
+ public Date getDate() {
+}
+```
+
+在模板查询的时候,将会翻译成
+
+```javascript
+@if(!isEmpty(minDate)){
+ and date>=#minDate#
+@}
+@if(!isEmpty(maxDate)){
+ and date<#maxDate#
+@}
+```
+
+> #### 注意
+>
+> minDate,maxDate 是俩个额外的变量,需要定义到pojo类里,DateTemplate也可以有默认值,如果@DateTemplate(),相当于@DateTemplate(accept="min日期字段,max日期字段",compare=">=,<")
+
+
+
+#### 7.7. Mapper相关注解
+
+Mapper 是将sql模板文件映射成一个具体的Dao方法类,这样方位代码开发和维护
+
+Mapper中的注解,包括常用的 SqlStatement ,SqlStatementType ,Sql,Param 还有不常用的 RowSize ,RowStart,具体参考Mapper
+
+
+
+#### 7.8. ORMQuery
+
+beetlsql 支持在实体类上增加ORMQuery注解,这样对于实体的查询,会触发懒加载,从而实现ORM 查询功能,具体参考ORM 查询一章
+
+
+
+### 8. BeetlSQL 数据模型
+
+BeetlSQL是一个全功能DAO工具,支持的模型也很全面,包括
+
+- Pojo, 也就是面向对象Java Objec。Beetlsql操作将选取Pojoe属性和sql列的交集。额外属性和额外列将忽略.
+- Map/List, 对于一些敏捷开发,可以直接使用Map/List 作为输入输出参数
+
+```java
+List
@@ -61,7 +61,7 @@