续上篇
在前两篇文章《一种基于数据库+模板渲染的代码生成器——简介及数据库查询》以及《一种基于数据库+模板渲染的代码生成器——表结构暂存与类设计》中,我们介绍了如何通过mysql中的自有数据库information_schema
查询数据库结构,以及通过另外构建数据表gen_table
与gen_table_column
,暂存数据结构,暂存表所生成的类名,属性等等,减少后续生成代码预览与频繁生成的数据库压力与代码复杂度。
本篇我们继续阐述代码生成器的后续逻辑——模板渲染。
几种Java中常见的模板渲染插件
字符串格式化工具
最简单的,我们知道Java自带的格式化输出:
1
| System.out.printf("%f\n",pi);
|
一些常用的工具中也有一些非常好用的模板化字符串的方法。
以著名的开源库Hutool为例,其中的StrUtil中的函数用法如下:
1 2
| String template = "{}爱{},就像老鼠爱大米"; String str = StrUtil.format(template, "我", "你");
|
freemaker
这是笔者最常用的一个渲染引擎,功能非常强大,一方面可以使用它来渲染网页,邮件等显示页面,另一方面,也可以用它通过配置复杂的XML文档,导出为word等格式;当然,也可以使用其生成想要的源代码。
freemaker中的渲染使用示意图如下:
![image-20220107171402344](https://gitee.com/wieweicoding/kevinqimgs/raw/master/img/image-20220107171402344.png)
上图是Freemaker官网上的一张示意图,在模板中设置${name}
字段,在Java中通过对象或Map,设置其name
值,通过freemaker即可生成指定文件。其模板文件大多格式为.ftl
。
一个简单的使用代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| Map<String, String> dataMap = new HashMap<>(); dataMap.put("name", "姓名"); configuration = new Configuration(); configuration.setDefaultEncoding("utf-8"); configuration.setClassForTemplateLoading(com.backstage.export.template.Template.class, "/模板存储路径"); Template t = configuration.getTemplate("模板文件.ftl");
File outFile = new File(fileName); Writer out = null; FileOutputStream fos = null; fos = new FileOutputStream(outFile); OutputStreamWriter oWriter = new OutputStreamWriter(fos, "UTF-8"); out = new BufferedWriter(oWriter); t.process(dataMap, out); out.close(); fos.close();
|
Apache Velocity
与Freemaker相似,使用上相比Freemaker来说,稍微复杂一些。
1 2 3 4 5 6 7 8 9 10
| VelocityInitializer.initVelocity();
VelocityContext context = VelocityUtils.prepareContext(data);
Template tpl = Velocity.getTemplate("/模板路径/模板.file-suffix.vm", Constants.UTF8);
StringWriter sw = new StringWriter(); tpl.merge(context, sw); System.out.println(sw.toString());
|
在我们学习的若依系统中,其使用的是Apache Velocity。
首先,查询gen_table
表,将指定表的结构信息查询出来:
1 2
| GenTable table = genTableMapper.selectGenTableById(tableId);
|
需要注意的是,上述查询,不仅查出来gen_table
表,也包括gen_table_column
表的数据,这源于GenTable
对象设计为:
1 2 3 4 5 6 7 8 9 10
| public class GenTable extends BaseEntity { @Valid private List<GenTableColumn> columns; }
|
然后准备模板渲染对象VelocityContext
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
|
public static VelocityContext prepareContext(GenTable genTable) { VelocityContext velocityContext = new VelocityContext(); velocityContext.put("tplCategory", genTable.getTplCategory()); velocityContext.put("tableName", genTable.getTableName()); velocityContext.put("functionName", StringUtils.isNotEmpty(functionName) ? functionName : "【请填写功能名称】"); velocityContext.put("ClassName", genTable.getClassName()); velocityContext.put("className", StringUtils.uncapitalize(genTable.getClassName())); velocityContext.put("moduleName", genTable.getModuleName()); velocityContext.put("columns", genTable.getColumns()); return velocityContext; }
|
代码生成器的模板还是十分复杂的,我们选其中一个Domain的模板来看其中的一部分:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| package ${packageName}.domain;
#foreach ($import in $importList) import ${import}; #end import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; import com.ruoyi.common.annotation.Excel; #if($table.crud || $table.sub) import com.ruoyi.common.core.domain.BaseEntity; #elseif($table.tree) import com.ruoyi.common.core.domain.TreeEntity; #end
/** * ${functionName}对象 ${tableName} * * @author ${author} * @date ${datetime} */ #if($table.crud || $table.sub) #set($Entity="BaseEntity") #elseif($table.tree) #set($Entity="TreeEntity") #end public class ${ClassName} extends ${Entity} { private static final long serialVersionUID = 1L;
#foreach ($column in $columns) #if(!$table.isSuperColumn($column.javaField)) /** $column.columnComment */ #if($column.list) #set($parentheseIndex=$column.columnComment.indexOf("(")) #if($parentheseIndex != -1) #set($comment=$column.columnComment.substring(0, $parentheseIndex)) #else #set($comment=$column.columnComment) #end #if($parentheseIndex != -1) @Excel(name = "${comment}", readConverterExp = "$column.readConverterExp()") #elseif($column.javaType == 'Date') @JsonFormat(pattern = "yyyy-MM-dd") @Excel(name = "${comment}", width = 30, dateFormat = "yyyy-MM-dd") #else @Excel(name = "${comment}") #end #end private $column.javaType $column.javaField;
#end #end
}
|
输出:
1 2 3 4 5
| StringWriter sw = new StringWriter(); Template tpl = Velocity.getTemplate(template, Constants.UTF8); tpl.merge(context, sw); dataMap.put(template, sw.toString());
|
总结
至此,我们就大概了解了一个代码生成器是如何将表结构列信息等一步一步转换为可以使用的源代码的过程。