为什么80%的码农都做不了架构师?>>>
当下jOOQ和MyBatis都是热门的JPA替代品,相对于JPA来说它们也更加注重于SQL本身。它们之间最明显的区别包括:
- jOOQ本质上是通过Java流式API使用领域专用语言(domain-specific language)来构造SQL
- MyBatis则基于XML的SQL模板映射,它的动态SQL是通过XML-DSL生成的
MyBatis当前的成功大部分是因为JPA本身充满争议(JPA还需要证明自己优于JDO),而MyBatis在这个时间窗提供了一个可选的、容易被SQL爱好者们接受的方案:
- 将Java与SQL完全分离,SQL代码在一个独立文件中。便于让DBA在生产环境调整、优化SQL。
- 能自动将结果集封装为对象。和动态sql一样,都是通过xml DSL实现。
用jOOQ实现SQL模板
jOOQ也能做同样的事情。和MyBatis不同的是,jOOQ的SQL模板(jOOQ 3.2)并没有使用专用的模板语言。将自由选择的权利留给用户自己。目前已集成的包括:
- Apache Velocity
- StringTemplate
- Freemarker
- Eclipse's Xtend
- XSLT
- 甚至提供一个自有的MyBatis模板适配器,用来将MyBatis迁移到jOOQ
看一个Velocity的例子。在where查询中加入了一个ID的列表作为动态参数:
SELECT
a.first_name,
a.last_name,
count(*)
FROM
t_author a
LEFT OUTER JOIN
t_book b ON a.id = b.author_id
WHERE
1 = 0
#foreach ($param in $p)
OR a.id = ?
#end
GROUP BY
a.first_name,
a.last_name
ORDER BY
a.id ASC
jOOQ的模板实现可以接受上面的模板,使用确定的输入对象来构造具体的jOOQ QueryPart。QueryPart是用于渲染SQL、绑定变量的对象:
class VelocityTemplate
implements org.jooq.Template {
private final String file;
public VelocityTemplate(String file) {
this.file = file;
}
@Override
public QueryPart transform(Object... input) {
// Velocity code
// -----------------------------------------
URL url = this.getClass().getResource(
"/org/jooq/test/_/templates/");
File file = url.getFile();
VelocityEngine ve = new VelocityEngine();
ve.setProperty(RESOURCE_LOADER, "file");
ve.setProperty(FILE_RESOURCE_LOADER_PATH,
new File(file ).getAbsolutePath());
ve.setProperty(FILE_RESOURCE_LOADER_CACHE,
"true");
ve.init();
VelocityContext context = new VelocityContext();
context.put("p", input);
StringWriter writer = new StringWriter();
ve.getTemplate(file, "UTF-8")
.merge(context, writer);
// jOOQ Code
// -----------------------------------------
return DSL.queryPart(writer.toString(), input);
}
}
就是这么简单。你可以随意控制自己的模板引擎适配器,向其中自由加入缓存、对象池等等。
然后上面的模板可以在任何jOOQ API允许使用普通SQL的地方使用了。如一个高层query:
Template tpl = new VelocityTemplate(
"authors-and-books.vm");
DSL.using(configuration)
.resultQuery(tpl, 1, 2, 3)
.fetch();
或者来一个类型安全的DSL嵌套查询:
DSL.using(configuration)
.select()
.from(new TableSourceTemplate("my-table.vm"))
.fetch();
当然你可以利用jOOQ的结果映射,它允许你实现自定义的表格——对象转换算法。这通常比生硬的XML更加友好(如MyBatis):
List result =
DSL.using(configuration)
.select()
.from(new TableSourceTemplate("my-table.vm"))
.fetch(new RecordMapper() {
public MyType map(Record record) {
// Custom mapping logic here
}
});
Java 8:
List result =
DSL.using(configuration)
.select()
.from(new TableSourceTemplate("my-table.vm"))
.fetch((Record) -> new MyType().init(record));
可能性是最好的东西
SQL模板是一个强大的工具。如果你喜欢简单的,基于字符串的SQL,它可以随时调整,比如插入一个小循环或if语句,注入一些动态SQL子句。下面有一些这样的SQL引擎:
- MyBatis - Java SQL 模板引擎领导者
- jOOQ - Java SQL 生成器、代码与 SQL 整合的领导者
- ElSql - 一个轻量级的工具,由Stephen Colebourne实现
- JIRM – 轻量级工具, 由Adam Gent实现
在所有这些工具中,只有jOOQ鼓励用户使用自己的模板引擎,从而为模板扩展性提供无限可能。