基于 FreeMarker 模板配置一键生成目标类文件

Posted niaonao

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于 FreeMarker 模板配置一键生成目标类文件相关的知识,希望对你有一定的参考价值。

1. 前言

    业务层 service、控制层 controller 创建文件时存在大量的重复代码编写(copy and paste)劳动,对于可复用的代码编写可以提取出来定制 freemarker 模板来生成目标文件,减少重复工作。Java 基于 freemarker 模板编写可以输出特定模板文件,如图1.1、freemarker 模板使用示例图,Java 可以维护数据,freemarker Template 可以专注于数据展现,输出模板样式文件。

    此处配置可复用代码模板,来实现重复代码文件类一键生成。基于 freemarker 模板技术来生成、输出目标文件。

    这里仅实现了功能,可以通过运行 main 主函数一键生成,没有做前端页面来承接。(可复用代码模板:Entity.java、EntityDao.java、EntityService.java、EntityServiceImpl.java 等)
在这里插入图片描述

图1.1、freemarker 模板使用示例图

2. 设计实现

2.1 实现功能

    根据数据表,生成对应的实体类、持久化类、业务接口及业务实现类。

2.2 应用场景

    项目新建或项目迭代中,扩展设计新表时,维护表相关的业务类、控制层、持久化层、实体类等文件。

2.3 设计实现

    Maven 项目管理依赖,交互数据库读取指定表设计,根据 Template 模板配置生成目标文件。

3. 效果演示

    此处新建 Maven 项目,引入 freemarker 依赖,在项目中配置模板文件,以实体模板 entity.ftl 及业务实现模板 entityServiceImpl.ftl 为例。(Template 模板文件扩展名为 ftl。)

    以表 xxl_job_qrtz_trigger_group 为例,生成对应的实体类,持久化层,业务层类文件。
    表结构如下

CREATE TABLE `xxl_job_qrtz_trigger_group` (
	`id` INT(11) NOT NULL AUTO_INCREMENT,
	`app_name` VARCHAR(64) NOT NULL COMMENT '执行器AppName' COLLATE 'utf8_general_ci',
	`title` VARCHAR(12) NOT NULL COMMENT '执行器名称' COLLATE 'utf8_general_ci',
	`order` TINYINT(4) NOT NULL DEFAULT '0' COMMENT '排序',
	`address_type` TINYINT(4) NOT NULL DEFAULT '0' COMMENT '执行器地址类型:0=自动注册、1=手动录入',
	`address_list` VARCHAR(200) NULL DEFAULT NULL COMMENT '执行器地址列表,多地址逗号分隔' COLLATE 'utf8_general_ci',
	PRIMARY KEY (`id`) USING BTREE
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB
AUTO_INCREMENT=1;

3.1 实体类模板配置及模板文件生成

3.1.1 模板文件 entity.ftl
    封装下划线驼峰转换函数 dashedToCamel()。

    freemarker 内建函数参考freemarker 内建函数
    比如此处引用 cap_first、uncap_first 等函数。

cap_first: 字符串中的首单词的首字母大写
uncap_first: 字符串中的首单词的首字母小写

<#assign aDate = .now?date>
<#function dashedToCamel(s)>
	<#return s
	?replace('(^_+)|(_+$)', '', 'r')
	?replace('\\\\_+(\\\\w)?', ' $1', 'r')
	?replace('([A-Z])', ' $1', 'r')
	?capitalize
	?replace(' ' , '')
	?uncap_first
	>
</#function>
package pers.niaonao.generate.freemarker.entity;

import java.util.*;

/**
 * @description 实体类
 * @date ${aDate?iso_utc}
 * @author niaonao
 */
public class ${model.className} {

	private static final long serialVersionUID = 1L;

<#list model.columnsName?keys as column>
	/**
	 *   ${model.columnsName[column].columnComment!}
	 *  column: ${column}
	 */
	private ${model.columnsName[column].columnType!} ${dashedToCamel(model.columnsName[column].columnName)};
</#list>

<#list model.columnsName?keys as column>
	/**
	 *   ${model.columnsName[column].columnComment!}
	 *  column: ${column}
	 */
	public ${model.columnsName[column].columnType!} get${dashedToCamel(model.columnsName[column].columnName)?cap_first}(){
		return ${dashedToCamel(model.columnsName[column].columnName)};
	}
	public void set${dashedToCamel(model.columnsName[column].columnName)?cap_first} (${model.columnsName[column].columnType!} ${dashedToCamel(model.columnsName[column].columnName)}){
		this.${dashedToCamel(model.columnsName[column].columnName)} = ${dashedToCamel(model.columnsName[column].columnName)};
	}
</#list>

}

3.1.2 目标文件生成 JobQrtzTriggerGroup.java

package pers.niaonao.generate.freemarker.entity;

import java.util.*;

/**
 * @description 实体类
 * @date 2021-07-13
 * @author niaonao
 */
public class JobQrtzTriggerGroup {

	private static final long serialVersionUID = 1L;

	/**
	 *   执行器地址列表,多地址逗号分隔
	 *  column: address_list
	 */
	private String addressList;
	/**
	 *   执行器地址类型:0=自动注册、1=手动录入
	 *  column: address_type
	 */
	private Integer addressType;
	/**
	 *   执行器AppName
	 *  column: app_name
	 */
	private String appName;
	/**
	 *   
	 *  column: id
	 */
	private Integer id;
	/**
	 *   排序
	 *  column: order
	 */
	private Integer order;
	/**
	 *   执行器名称
	 *  column: title
	 */
	private String title;

	/**
	 *   执行器地址列表,多地址逗号分隔
	 *  column: address_list
	 */
	public String getAddressList(){
		return addressList;
	}
	public void setAddressList (String addressList){
		this.addressList = addressList;
	}
	/**
	 *   执行器地址类型:0=自动注册、1=手动录入
	 *  column: address_type
	 */
	public Integer getAddressType(){
		return addressType;
	}
	public void setAddressType (Integer addressType){
		this.addressType = addressType;
	}
	/**
	 *   执行器AppName
	 *  column: app_name
	 */
	public String getAppName(){
		return appName;
	}
	public void setAppName (String appName){
		this.appName = appName;
	}
	/**
	 *   
	 *  column: id
	 */
	public Integer getId(){
		return id;
	}
	public void setId (Integer id){
		this.id = id;
	}
	/**
	 *   排序
	 *  column: order
	 */
	public Integer getOrder(){
		return order;
	}
	public void setOrder (Integer order){
		this.order = order;
	}
	/**
	 *   执行器名称
	 *  column: title
	 */
	public String getTitle(){
		return title;
	}
	public void setTitle (String title){
		this.title = title;
	}

}

3.2 业务实现类模板配置及模板文件生成

3.2.1 模板文件 entityServiceImpl.ftl

<#assign aDate = .now?date>
package pers.niaonao.generate.freemarker.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import pers.niaonao.generate.freemarker.dao.${model.className}Dao;
import pers.niaonao.generate.freemarker.entity.${model.className};

import java.util.List;

/**
 * @description 业务层实现
 * @date ${aDate?iso_utc}
 * @author niaonao
 */
@Service
public class ${model.className}ServiceImpl implements ${model.className}Service {

	/**
	* 引入持久化层
	*/
	private ${model.className}Dao ${model.className?uncap_first }Dao;

	@Autowired
	public ${model.className}ServiceImpl(${model.className}Dao ${model.className?uncap_first }Dao) {
		this.${model.className?uncap_first }Dao = ${model.className?uncap_first }Dao;
	}

	public List<${model.className}> findAllList() {
		return ${model.className?uncap_first }Dao.findAllList();
	}
	public ${model.className} getById(Integer id) {
		return ${model.className?uncap_first }Dao.getById(id);
	}
}

3.2.2 生成目标文件 JobQrtzTriggerGroupServiceImpl.java

package pers.niaonao.generate.freemarker.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import pers.niaonao.generate.freemarker.dao.JobQrtzTriggerGroupDao;
import pers.niaonao.generate.freemarker.entity.JobQrtzTriggerGroup;

import java.util.List;

/**
 * @description 业务层实现
 * @date 2021-07-13
 * @author niaonao
 */
@Service
public class JobQrtzTriggerGroupServiceImpl implements JobQrtzTriggerGroupService {

	/**
	* 引入持久化层
	*/
	private JobQrtzTriggerGroupDao jobQrtzTriggerGroupDao;

	@Autowired
	public JobQrtzTriggerGroupServiceImpl(JobQrtzTriggerGroupDao jobQrtzTriggerGroupDao) {
		this.jobQrtzTriggerGroupDao = jobQrtzTriggerGroupDao;
	}

	public List<JobQrtzTriggerGroup> findAllList() {
		return jobQrtzTriggerGroupDao.findAllList();
	}
	public JobQrtzTriggerGroup getById(Integer id) {
		return jobQrtzTriggerGroupDao.getById(id);
	}
}

4. 代码实现

在这里插入图片描述

图4.1、项目结构图
  • doc 文件夹下存放数据库演示脚本文件;
  • dao、entity、service 包分别作为不同模板的目标文件生成路径;
  • helper 包下包含此次 freemarker 模板配置、模板加载、目标文件生成的功能实现,主方法入口为 ModelGenerator.main() 方法。
    • helper.constant 维护常量类;
    • helper.enums 维护枚举类,配置模板标识、模板文件路径及生成目标文件路径的映射管理;
    • helper.template 维护模板文件;
    • helper.util 维护工具类,此处功能实现根据表设计,生成对应的业务类文件,封装了表字段数据类型转换工具类等;

4.1 新建项目配置依赖

    新建 Maven 项目,配置依赖原理
pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>pers.niaonao</groupId>
    <artifactId>generate-code-template</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>

        <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.8</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.3.3</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.25</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
        <!-- 数据库连接池 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.2.6</version>
        </dependency>

        <!-- Apache FreeMarker 用于生成通用文本的模板引擎 -->
        <!-- https://mvnrepository.com/artifact/org.freemarker/freemarker -->
        <dependency>
            <groupId>org.freemarker</groupId>
            <artifactId>freemarker</artifactId>
            <version>2.3.30</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
        <!-- 开源工具库 -->
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>30.1.1-jre</version>
        </dependency>
    </dependencies>
</project>

4.2 FreeMarker 模板配置关键方法说明

    freemarker 配置信息可在 Configuration 实例中配置,也可在 Template 实例中覆盖掉。一般通过 Configuration.setter() 方法来配置,通常建议在 Configuration 实例中配置,且不建议在 Template 实例覆盖配置。
    例入:

    Configuration myCfg = new Configuration(Configuration.VERSION_2_3_30);
    myCfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
    myCfg.setDefaultEncoding("UTF-8");

    通过 Template.process(…) 方法加载模板配置输出目标文件;

    具体的模板可通过 Configuration.getTemplate(…) 获取。

	/** 
	 * dataModel:java model 携带数据到模板
	 * out:输出到目标文件
	 * 
	 * String templateName = "模板文件全路径";
	 * Configuration configuration = new Configuration(Configuration.VERSION_2_3_30);
	 * Template template = configuration.getTemplate(templateName);
	 * template.process(dataModel, out);
	 */
    public void process(Object dataModel, Writer out) throws TemplateException, IOException {
        this.createProcessingEnvironment(dataModel, out, (ObjectWrapper)null).process();
    }

    如果直接使用 Template.process() 方法报错,可以手工配置实例 Environment 后调用方法 process() 输出目标文件。

	Environment env = myTemplate.createProcessingEnvironment(root, out);
	env.setLocale(java.util.Locale.ITALY);
	env.setNumberFormat("0.####");
	env.process();  // process the template

4.3 功能实现主要文件说明

4.3.1 枚举类维护模板文件及目标文件生成路径

GenerateTemplateEnum.java

package pers.niaonao.generate.freemarker.helper.enums;

/**
 * @description: 模板枚举类
 * @author niaonao
 **/
public enum GenerateTemplateEnum {

    /**
     * 枚举值包括模板标识,模板文件,生成目标路径
     */
    GENERATE_MODEL("entity","entity.ftl", "/entity/"),
    GENERATE_DAO("entityDao","entityDao.ftl", "/dao/"),
    GENERATE_SERVICE("entityService","entityService.ftl", "/service/"),
    GENERATE_SERVICE_IMPL("entityServiceImpl","entityServiceImpl.ftl", "/service/");

    /**
     * code
     */
    private String code;
    /**
     * 生成目标文件路径
     */
    private String targetDir;
    /**
     * 模板文件
     */
    private String templateFtl;

    GenerateTemplateEnum(String code, String templateFtl, String targetDir) {
        this.code = code;
        this.templateFtl = templateFtl;
        this.targetDir = targetDir;
    }

    public String getCode() {
        return code;
    }

    public String getTargetDir() {
        return targetDir;
    }

    public String getTemplateFtl() {
        return templateFtl;
    }

    public static GenerateTemplateEnum getEnumValue(String code) {
        for (GenerateTemplateEnum constants : values()) {
            if (constants.getCode().equals(code)) {
                return constants;
            }
        }
        return null;
    }
}

4.3.2 表字段数据类型转换类
    将数据表字段数据类型转换为 Java 数据类型。

DbDataTypeConvertUtil.java

package pers.niaonao.generate.freemarker.helper.util;

import java.util.Map;
import java.util.TreeMap;

/**
 * @description: 表字段类型转换
 * @author niaonao
 **/
public class DbDataTypeConvertUtil {
    /**
     * Name to value
     */
    private static Map<String, String> jdbcTypes;

    static {
        jdbcTypes = new TreeMap<String, String>();
        jdbcTypes.put("varchar", "String");
        jdbcTypes.put("char", "String");
        jdbcTypes.put("longtext", "String");
        jdbcTypes.put("text", "String");
        jdbcTypes.put("blob", "java.lang.byte[]");
        jdbcTypes.put("text", "String");
        jdbcTypes.put("integer", "Long");
        jdbcTypes.put("int", "Integer");
        jdbcTypes.put("tinyint", "Integer");
        jdbcTypes.put("smallint", "Integer");
        jdbcTypes.put("mediumint", "Integer");
        jdbcTypes.put("bit", "Boolean");
        jdbcTypes.put("bigint", "Long");
        jdbcTypes.put("float", "Float");
        jdbcTypes.put("double", "Double");
        jdbcTypes.put("decimal", "BigDecimal");
        jdbcTypes.put("boolean", "Integer")基于 FreeMarker 模板配置一键生成目标类文件

Freemarker

使用FreeMarker生成Word文档

freemarker生成go语言

springboot2.0结合freemarker生成静态化页面

Java 使用模板生成 Word 文件---基于 Freemarker 模板框架