5.13 SQL 管理与动态生成

  1. JFinal利用自带的Template Engine极为简洁的实现了Sql管理功能。一如既往的极简设计,仅有#sql、#para、#namespace三个指令,学习成本依然低到极致。
  2. **重要**:除了以上三个 sql 管理专用指令以外,jfinal 模板引擎的所有指令和功能也可以用在 sql 管理,jfinal 模板引擎用法见第 6 章:[http://www.jfinal.com/doc/6-1](http://www.jfinal.com/doc/6-1)

1、基本配置

  1. ActiveRecordPlugin中使用sql管理功能示例代码如下:
  1. ActiveRecordPlugin arp = new ActiveRecordPlugin(druidPlugin);
  2. arp.getEngine().setSourceFactory(new ClassPathSourceFactory());
  3. arp.addSqlTemplate("all.sql");
  4. _MappingKit.mapping(arp);
  5. me.add(arp);
  1. 如上例所示,arp.getEngine().setSourceFactory(new ClassPathSourceFactory()) 这行代码,设置了模板引擎将从 class path 或者 jar 包中读取 sql 文件。 可以将 sql 文件放在maven项目下的resources 之下,编译器会自动将其编译至 class path 之下,进而可以被读取到。
  2. 第三行代码通过 arp.addSqlTemplate(…) 添加外部 sql 模板文件,可以通过多次调用addSqlTemplate来添加任意多个外部 sql 文件,并且对于不同的 ActiveRecordPlugin 对象都是彼此独立配置的,有利于多数据源下对 sql 进行模块化管理。
  3. 如果希望在开发阶段可以对修改的sql文件实现热加载,可以配置arp.setDevMode(true)或者arp.getEngine().setDevMode(true),如果不配置则默认使用configConstant中的me.setDevMode(…)配置。
  4. 特别注意:sql管理模块使用的模板引擎并非在 configEngine(Engine me)配置,因此在配置shared methoddirective 等扩展时需要使用activeRecordPlugin.getEngine(),然后对该Engine对象进行配置。

2、#sql 指令

  1. 通过#sql指令可以定义sql语句,如下是代码示例:
  1. #sql("findGirl")
  2. select * from girl where age > ? and age < ? and weight < 50
  3. #end
  1. 上例通过 #sql 指令在模板文件中定义了key值为“findGirl”的sql语句,在java 代码中的获取方式如下:
  1. String sql = Db.getSql("findGirl");
  2. Db.find(sql, 16, 23);
  1. 上例中第一行代码通过Db.getSql()方法获取到定义好的sql语句,第二行代码直接将sql用于数据库查询。

此外,还可以通过Model.getSql(key)方法来获取sql语句,功能与Db.getSql(key)基本一样,唯一不同的是为多数据源分别配置了sql模板的场景:

  • Model.getSql()在自身所对应的ActiveRecordPlugin的sql模板中去取sql

  • Db.getSql()在主ActiveRecordPlugin的sql模板中去取sql

  • 可通过Db.use(…).getSql(…) 实现Model.getSql()相同功能

    也就是说,多数据源之下,可为不同的ActiveRecordPlugin对象分别去配置sql模板,有利于模块化管理。

3、#para 指令

  1. #para 指令用于生成 sql 中的问号占位符以及问号占位符所对应的参数值,两者分别保存在 SqlPara对象的 sql 和 paraList 属性之中。
  2. #para指令支持两种用法,一种是传入 **int型常量参数**的用法,如下示例展示的是int型常量参数的用法:
  1. #sql("findGirl")
  2. select * from girl where age > #para(0) and weight < #para(1)
  3. #end
  1. 上例代码中两个 #para 指令,传入了两个 int 型常量参数,所对应的java后端代码必须调用getSqlPara(String key, Object… paras),如下是代码示例:
  1. SqlPara sqlPara = Db.getSqlPara("findGirl", 18, 50);
  2. Db.find(sqlPara);
  1. 以上第一行代码中的 18 50 这两个参数,分别被前面 #sql 指令中定义的 #para(0) 与 #para(1) 所使用。
  2. Db.getSqlPara(String key, Object... paras) 方法的第二个参数 Object... paras,在传入实际参数时,下标值从 0 开始算起与 #para(int) 指令中使用的 int 型常量相对应。
  3. #para 指令的另一种用法是传入除了 int 型常量以外的任意类型参数(注意:两种用法处在同一个 #sql 指令之中时只能选择其中一种),如下是代码示例:
  1. #sql("findGirl")
  2. select * from girl where age > #para(age) and weight < #para(weight)
  3. #end
  1. 与上例模板文件配套的java代码如下所示:
  1. Kv cond = Kv.by("age", 18).set("weight", 50);
  2. SqlPara sp = Db.getSqlPara("findGirl", cond);
  3. Db.find(sp);
  1. 上例代码获取到的SqlPara对象sp中封装的sql为:select * from girl where age &gt; ? and weight &lt; ?,封装的与sql问号占位符次序一致的参数列表值为:[18, 50]。
  2. 以上两个示例,获取到的SqlPara对象中的值完全一样。其中的sql值都为:select * from girl where age &gt; **?** and weight &lt; **?**,其中的参数列表值也都为 [1850]。不同的是 #para 用法不同,以及它们对应的java代码传参方式不同,前者传入的是Object… paras参数,后者是Map data参数。
  3. 切记: #para 指令所到之处**永远是生成一个问号占位符**,并不是参数的值,参数值被生成在了SqlPara对象的paraList属性之中,通过sqlPara.getPara()可获取。如果想生成参数值用一下模板输出指令即可:#(value)
  4. **极其重要的通用技巧**:如果某些数据库操作API不支持SqlPara参数,而只支持String sqlObject paras这两个参数,可以这样来用:method(sqlPara.getSql(), sqlPara.getPara())。这样就可以让所有这类API都能用上Sql管理功能。
  5. **加餐:**有些同学希望在 sql 文件中获取getSqlPara(String, Object paras) 方法传入的paras参数,可以通过表达式 **_PARA_ARRAY_[index]** 来获取到下标为index的参数值。
  6. 由于经常有人问到 mysql 数据库 sql 语句的 like 子句用法,补充如下示例:
  1. #sql("search")
  2. select * from article where title like concat('%', #para(title), '%')
  3. #end
  1. 以上示例的like用法完全是 JDBC 决定的,JFinal仅仅是生成了如下sql而已:
  2. select * from article where title like concat(&#39;%&#39;, ?, &#39;%&#39;),也就是仅仅将 #para(title) 替换生成为一个问号占位 ”?” 而已。

4、#namespace 指令

  1. #namespace 指令为 sql 语句指定命名空间,不同的命名空间可以让#sql指令使用相同的key值去定义sql,有利于模块化管理,如下所示:
  1. #namespace("japan")
  2. #sql("findGirl")
  3. select * from girl where age > ? and age < ? and weight < 50
  4. #end
  5. #end
  1. 上面代码指定了namespace为”japan”,在使用的时候,只需要在key前面添加namesapce值前缀 + 句点符 + key即可:
  1. getSql("japan.findGirl");

5、分页用法

  1. Sql管理实现分页功能,在使用#sql定义sql时,与普通查询完全一样,不需要使用额外的指令,在java代码中使用getSqlPara得到SqlPara对象以后,直接扔给Db或者Modelpaginate方法就打完收工了,以下是代码示例:
  1. SqlPara sqlPara = Db.getSqlPara("findGirl", 18, 50);
  2. Db.paginate(1, 10, sqlPara);
  1. 如以上代码所示,将sqlPara对象直接用于paginate方法即可,而#sql定义与普通的非分页sql定义完全相同。
  2. 传统而平庸的sql管理框架实现分页功能额外引入很多无聊的概念,例如需要引入page指令、orderBy指令、select指令或者各种tag等无聊的东西,徒增很多学习成本,浪费广大开发者的生命。

6、高级用法

  1. 除了#sql、#para、#namespace之外,还可以使用JFinal Template Engine中所有存在的指令,生成复杂条件的sql语句,以下是相对灵活的示例:
  1. #sql("find")
  2. select * from girl
  3. #for(x : cond)
  4. #(for.first ? "where": "and") #(x.key) #para(x.value)
  5. #end
  6. #end
  1. 以上代码#for指令对Map类型的cond参数进行迭代,动态生成自由的查询条件。上图中的三元表达式表示在第一次迭代时生成 where,后续则生成 and 。#(x.key) 输出参数 key 值,#para(x.value) 输出一个问号占位符,以及将参数 value 值输出到 SqlPara.paraList 中去。
  2. 以上sql 模板所对应的 java 代码如下:
  1. Kv cond = Kv.by("age > ", 16).set("sex = ", "female");
  2. SqlPara sp = Db.getSqlPara("find", Kv.by("cond", cond));
  3. Db.find(sp);
  1. 以上第一行代码是 JFinal 独创的参数带有比较运算符的用法,可以同时生成sql查询条件名称、条件运算符号、参数列表,一石三鸟。甚至可以将此法用于 and or not再搭配一个 LinkedHashMap 生成更加灵活的逻辑组合条件sql
  2. 更加好玩的用法,可以用jfinal 模板引擎的 #define 指令将常用的 sql 定义成通用的模板函数,以便消除重复性 sql 代码。总之,利用 #sql、#para、#namespace 这三个指令再结合模板引擎已有指令自由组合,可非常简洁地实现极为强大的 sql管理功能。
  3. 注意:以上例子中的Kv JFinal 提供的用户体验更好的 Map 实现,使用任意的 Map 实现都可以,不限定为Kv

< 5.12 Oracle支持

5.14 多数据源支持 >

原文: http://www.jfinal.com/doc/5-13