Codgen是一个基于数据库元数据模型,使用freemarker模板引擎来构建输出的代码生成器。freemarker的数据模型结构通常来说都是一个Map树状结构模型,codgen也不例外,它的数据模型这棵树的根节点一般包含一个TableModel(表数据模型)对象。TableModel顾名思义就是由一张数据库表的元数据信息组成的一个数据模型,有了这个数据模型,再加上一套使用freemarker编写的多层架构模板,就可以生成一套基于这个表的多层架构代码文件。
总的来说,codgen具有以下主要功能及特性: 采用freemarker的构建公式:数据模型+模板=输出,默认使用freeMarker模板引擎来生成代码,但可以通过Builder接口实现其他构建方式。 核心数据模型TableModel基于JDBC实现表元数据及其所有字段列的相关元数据信息的封装。 数据模型TableModel基于JDBC实现,可以取得大部分元数据信息,个别信息的取得与具体数据库方言有关,可以通过扩展DbProvider?来实现。 通过实现接口ColumnHandler,可以完成JDBC数据类型到各种编程语言的类型转换操作及更多复杂的操作,如处理Oracle的大写列名以增强列名称的可读性。 通过配置可以动态增加或重定义数据模型,并可以被后面的数据模型通过模板语言引用或组装。 构建时指定的模板可以是一段文本字符串,也可以是一个文件路径,并且它们的内容里都可以引用已定义的数据模型。 构建时指定的输出类型可以是文本,也可以是文件,指定的文件输出路径也可以引用数据模型变量。 项目配置引入继承机制,这样就可以重用在父类配置中已定义的数据模型及其他配置信息。 项目配置信息可以分开多个配置文件存放,codgen一次性加载并缓存起来以加速之后的访问。
由于codgen不是一个独立的项目,本着不重新发明轮子的原则,依赖于一些比较流行的开源框架。因此除了需要下载最新的codgen.jar包文件以外,还要下载以下jar文件:
commons-lang.jar 或以上兼容版本 commons-logging.jar或以上兼容版本 log4j-1.2.14或以上兼容版本 freemarker-2.3.13或以上兼容版本 除了以上4个必须依赖的开源包以外,由于codgen的TableModel?元数据是通过JDBC来获取的,还需要下载对应数据库方言的JDBC驱动包,比如SQLServer2005的JDBC驱动包是sqljdbc.jar。目前codgen默认只支持Oracle10g/SQLServer2000/SQLServer2005三种数据库版本。但是可以通过扩展实现codgen提供的DbProvider?接口来支持更多的数据库版本。 Oracle10g的JDBC驱动包:ojdbc14.jar SQLServer2000的JDBC驱动包:msbase.jar、mssqlserver.jar和msutil.jar SQLServer2005的JDBC驱动包:sqljdbc.jar 注:JDK1.6.0_13或以上兼容版本
虽然不使用配置文件的方式也可以实现整个代码的构建操作,但是为了减少使用代码的编写量及方便日后维护,建议使用配置文件来声明构建所依赖的一些信息,譬如‘数据模型’变量声明,‘输出模型’变量声明,以及数据库信息提供者等一些可以与代码分离的配置信息。以下就是一段比较完整的配置文件:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE codgen-config PUBLIC "-//Apache Software Foundation//DTD Tengen Configuration 1.0//EN" "com/bcs/codgen/resources/codgen-config_1_0.dtd"> <codgen-config> <project name="AOWork" label="工程验收" outputEncoding="GBK" isDefault="true" extends="defaultProject"> <dbProvider class="com.bcs.codgen.service.impl.Sql2005Provider"> <jdbcConfig> <driver>com.microsoft.sqlserver.jdbc.SQLServerDriver</driver> <url>jdbc:sqlserver://127.0.0.1/SQL2005;DatabaseName=MUCM_DG;selectMethod=cursor</url> <user>sa</user> <password>sa</password> </jdbcConfig> <columnHandler class="com.bcs.codgen.service.impl.DataTypeConverterForJava"/> </dbProvider> <dataModel name="templateDirectory">template/AOWork</dataModel> <dataModel name="outputDirectory">D:/QtoneProject/TengenCode/${copyright.author}/${projectName}</dataModel> <dataModel name="NamespaceModel" >com.qtone.aow.model.${groupName}</dataModel> <dataModel name="NamespaceParam">com.qtone.aow.model.${groupName}</dataModel> <dataModel name="NamespaceDao">com.qtone.aow.dao.${groupName}</dataModel> <dataModel name="NamespaceDaoImpl">com.qtone.aow.dao.${groupName}</dataModel> <dataModel name="NamespaceBll">com.qtone.aow.bll.${groupName}</dataModel> <dataModel name="NamespaceBllImpl">com.qtone.aow.bll.${groupName}</dataModel> <dataModel name="NamespaceApp">com.qtone.aow.app.${groupName}</dataModel> <template name="tDao" type="file">dao.ftl</template> <output name="Model" type="text" templateText="${defaultJavaModel}" ></output> <output name="Param" type="text" templateFile="Param.ftl" ></output> <output name="Dao" type="text" templateName="tDao"></output> <output name="DaoImpl" type="text"></output> <output name="Bll" type="file" templateFile="template/AOWork/Bll">${outputDirectory}/Bll.java</output> <output name="BllImpl" type="file">${outputDirectory}/BllImpl.java</output> </project> </codgen-config>
一个项目就是一个最小构建单位。
该节点用于配置当前项目所使用的数据库信息提供者,非强制性配置节点。dbProvider一般包含一个jdbcConfig和一个或多个columnHandler等子节点,但是这些也不是强制需要配置的,可以在编程里面再指定,譬如jdbcConfig在不配置的情况下,可以在编程的时候在指定一个JdbcConfig对象或者直接传递一个数据库连接Connection对象即可。也就是说DbProvider分别提供了JdbcConfig和Connection作为参数的两种构造方法。DbProvider配置节点只有一个class属性,该属性指定的就是具体某种数据库方言的一个数据库元数据信息提供者的一个实现,譬如OracleProvider就是一个DbProvider的实现类,用于提供Oracle数据库相关的一些元数据信息。codgen还实现了MsSQL等一些数据库的DbProvider,当然,你也可以通过继承DbProvider来扩展自己的数据库信息提供者,这样的工作也很简单,毕竟DbProvider抽象类已经实现了大部分的功能,你只需要实现一两个抽象方法就可以扩展出一个自己的dbProvider,在配置里面使用class属性指定该类的完全限定名称即可。
该节点用于指定JDBC连接的配置信息,包含driver、url、user和password等子节点。
columnHandler指的是列模型处理器。它是dbProvider下的子节点,一个数据库信息提供者可以包含多个列模型处理器,在创建表模型时从数据库里取得相关字段列信息并封装成一个列模型对象后,这些处理器就会被调用来进行后续的一些特殊处理操作,比如根据把数据库字段的数据类型转换成实际编程语言的数据类型,再如Oracle的所有字段名称都是大写的,在这里可以增加一个处理器负责把这些大写的名称统一转换成可读性比较强的列名称,如下面代码所示: ProjectConfig projectConfig = ProjectConfigHelper.getDefaultProjectConfig(null);
//增加一个额外的列模型处理器,处理Oracle的大写列名以增强列名称的可读性
projectConfig.getDbProvider().getColumnHandlers().add(new ColumnHandler() { String[] columnNames = new String[]{"UserID","UserName","Sex","Remark"}; public void handle(ColumnModel columnModel) { for (int i = 0; i < columnNames.length; i++) { if(columnModel.getColumnName().equalsIgnoreCase(columnNames[i])){ columnModel.setColumnName(columnNames[i]); } } } }); buildConfig = new ProjectBuildConfig(projectConfig); buildConfig.setTableName("Sys_UserInfo");
虽然列模型处理器的只有一个handle方法,但这个简单的接口却可以实现很多灵活而强大的功能。
dataModel指的是数据模型,它是专门用来定义在模板里面需要被动态替换的一组数据。简单来说,就是一个键值对,name为键名称,节点里面包含的字符串就是它对应的值。如下面就是一个名称为“codgen”的数据模型:
<dataModel name="codgen">myCodgen</dataModel>
前面定义的数据模型可以被后面的数据模型嵌套引用,如下面的一个数据模型里面的内容就引用了上面的“codgen”:
<dataModel name="helloCodgen">hello,${codgen}!</dataModel>
那么经过解析后,就会输出内容: hello,myCodgen! 由以上的配置文件示例可以发现,目前在配置文件里通过dataModel标签只能定义一些简单级别的数据模型,如${copyright.author}这样的复杂对象级别的数据模型就需要在编程里面这样来定义:
Copyright copyright = new copyright(); copyright.setAuthor("黄天政"); dataModelMap.put("copyright",copyright); //dataModelMap就是一棵数据模型树
像以上copyright这样的数据模型,在数据模型树里属于一级节点,包括在配置文件里面声明的都属于一级数据模型节点。codgen除了提供一个TableModel比较核心的数据模型以外,还内置了其他一些数据模型,如下列表所述:
TableModel数据模型的名称并不是“tableModel”,而是“table”; 以上的数据模型都可以在配置文件里面直接引用,如下面一个数据模型的定义内容就引用了“copyright.author”和“projectName”两个模型:
<dataModel name="outputDirectory">D:/QtoneProject/TengenCode/${copyright.author}/${projectName}</dataModel>
template只有name和type两个属性,分别表示模板名称和模板的类型。模板类型也只有两个枚举值,一个是file类型;另一个是text类型。template并不是project必需的节点,一般在模板内容比较多,并且类型为text的时候才使用,当然也可以指定type为file类型,而这时节点里面的内容就是模板所在的路径,而不是模板文本内容。下面的代码片段就是一个关于版权信息的模板定义:
<template name="tModel" type="text"> <![CDATA[ ${defaultCopyright} ${defaultJavaModelWithNoCopyright} ]]> </template>
其中defaultCopyright和defaultJavaModelWithNoCopyright是codgen预先定义的两个数据模型。
output指明一个构建输出的目标,其属性及说明如下表所示:
//取得指定项目名称的配置信息 ProjectConfig projectConfig = ProjectConfigHelper.getProjectConfig(getServletContext(),projectName); //实例化一个项目构建配置对象 ProjectBuildConfig buildConfig = new ProjectBuildConfig(projectConfig); //设置数据库表名称(Sys_UserInfo) buildConfig.setTableName(tableName); //设置表标签(如:用户信息) buildConfig.setTableLabel(tableLabel); //设置分组名称(通常为表 名称的前缀,如:Sys) buildConfig.setGroupName(groupName); //设置模块名称(如:UserInfo) buildConfig.setModuleName(moduleName); //设置版本的创建人 buildConfig.getCopyright().setAuthor(author); //由一个构建配置对象实例化一个代码生成器对象 Builder builder = new CodeBuilder(buildConfig); //执行构建操作,返回构建结果 Map<String,OutputModel> omMap = builder.build();
在web环境下,codgen是这样去寻找配置文件来加载的:如果web.xml配置了名称为“codgen.config”的上下文初始化参数(相对于WEB-INF/classes目录下的文件路径,多个文件名以英文逗号分隔);如果未配置该参数,则默认读取类路径根目录下的"codgen-config.xml",所以在这种情况下请确保src目录下存在codgen-config.xml文件。以下代码片段说明了如何在web.xml里指定加载配置:
<context-param> <param-name>codgen.config</param-name> <param-value>codgen-config.xml,../config/codgen-Another.xml</param-value> </context-param>
其中“../config”为WEB-INF下的一个子目录,而“Another”是其中一个构建项目的名称,在“codgen-Another.xml”这个配置文件里只有一个project节点,名称就是“Another”。如果一个构建项目的配置信息比较多,建议按照项目名称来分开文件配置。
public static void main(String[] args) { //取得默认的项目配置信息,在非web环境下调用传递参数null即可,但配置文件必须是src目录下的codgen-config.xml ProjectConfig projectConfig = ProjectConfigHelper.getDefaultProjectConfig(null); ProjectBuildConfig buildConfig = new ProjectBuildConfig(projectConfig); buildConfig.setTableName("Sys_UserInfo"); //buildConfig.setTableLabel("用户信息"); //不设置默认为表注释,表注释为空时则使用表名称 //buildConfig.setGroupName("System"); //不设置默认为Sys //buildConfig.setModuleName("UserInfo"); //不设置默认为UserInfo //buildConfig.getCopyright().setAuthor("黄天政"); //不设置默认为当前计算机用户名 Builder builder = new CodeBuilder(buildConfig); Map<String,OutputModel> omMap = builder.build(); for(Entry<String,OutputModel> entry : omMap.entrySet()){ System.out.println("生成内容="+entry.getValue().getOutput()); } }