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

本文介绍了如何通过Freemarker模板技术,自动化地根据数据库表结构生成Java的实体类、DAO、Service和ServiceImpl,提升代码生成效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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");
        jdbcTypes.put("date", "Date");
        jdbcTypes.put("time", "Date");
        jdbcTypes.put("datetime", "Date");
        jdbcTypes.put("timestamp", "Date");
        jdbcTypes.put("year", "Date");
    }
    public static String getJavaType(String sqlType) {
        return jdbcTypes.get(sqlType);
    }
}

4.3.3 数据表字段模型封装
    用于 freemarker 模板生成文件时渲染字段数据信息
ColumnModel .java

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

/**
 * @description: 表字段 model
 * @author niaonao
 **/
public class ColumnModel {
    /**
     * 字段名称
     */
    private String columnName;
    /**
     * 字段数据类型
     */
    private String columnType;
    /**
     * 字段描述
     */
    private String columnComment;

    public String getColumnName() {
        return columnName;
    }

    public void setColumnName(String columnName) {
        this.columnName = columnName;
    }

    public String getColumnType() {
        return columnType;
    }

    public void setColumnType(String columnType) {
        this.columnType = columnType;
    }

    public String getColumnComment() {
        return columnComment;
    }

    public void setColumnComment(String columnComment) {
        this.columnComment = columnComment;
    }
}

4.3.4 数据表模型封装

TableModel.java

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

import java.util.Map;

/**
 * @description 实体类 Model
 * @author niaonao
 **/
public class TableModel {

    /**
     * entity 实体类名称
     */
    private String className;
    /**
     * entity 表名称
     */
    private String tableName;
    /**
     * 主键字段名称
     */
    private String primaryKey;
    /**
     * 表字段集合
     */
    private Map<String, ColumnModel> columnsName;

    public TableModel(String tableName, String primaryKey, String className, Map<String, ColumnModel> columnsName) {
        super();
        this.className = className;
        this.tableName = tableName;
        this.primaryKey = primaryKey;
        this.columnsName = columnsName;
    }

    public String getClassName() {
        return className;
    }

    public void setClassName(String className) {
        this.className = className;
    }

    public String getTableName() {
        return tableName;
    }

    public void setTableName(String tableName) {
        this.tableName = tableName;
    }

    public String getPrimaryKey() {
        return primaryKey;
    }

    public void setPrimaryKey(String primaryKey) {
        this.primaryKey = primaryKey;
    }

    public Map<String, ColumnModel> getColumnsName() {
        return columnsName;
    }

    public void setColumnsName(Map<String, ColumnModel> columnsName) {
        this.columnsName = columnsName;
    }
}

4.3.5 自动生成类文件实现
    (1)生成方法 autoGenerateModel(String… tableNames),支持传入多个表,遍历生成对应的业务类;
    (2)初始化配置信息方法 init() 维护数据库信息;
    (3)freemarker 配置实例 Configuration 信息;
    (4)配置生成目标文件类型,根据枚举类扩展维护;
    (5)支持去除业务表前缀,如 xxx_job_info 生成实体文件为 JobInfo.java 而不是 XxlJobInfo.java 。
    (6)读取数据表设计转换为封装模型方法 generate(),同时根据模型及 Template.process(…) 生成目标文件。

package pers.niaonao.generate.freemarker.helper;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.util.StringUtils;
import com.google.common.collect.ImmutableMap;
import freemarker.template.Configuration;
import freemarker.template.Template;
import java.io.*;
import java.sql.*;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.sql.DataSource;

import pers.niaonao.generate.freemarker.helper.constant.Global;
import pers.niaonao.generate.freemarker.helper.util.ColumnModel;
import pers.niaonao.generate.freemarker.helper.util.DbDataTypeConvertUtil;
import pers.niaonao.generate.freemarker.helper.util.TableModel;
import pers.niaonao.generate.freemarker.helper.enums.GenerateTemplateEnum;

/**
 * @description 模板类自动生成工具
 * freemarker 内建函数参考文档: http://freemarker.foofun.cn/ref_builtins_string.html
 * @author niaonao
 */
public class ModelGenerator {
    private static String jdbcUrl;
    private static String username;
    private static String password;
    private static String SHOW_TABLE_NAME = "show tables";
    private static String SHOW_COLUMN_INFO =
            "select column_name,column_comment,data_type,column_key from information_schema.columns where table_name = '${tableName}'";
    private final String TEMPLATE_NAME = "TEMPLATE_NAME";
    private final String FILE_NAME = "FILE_NAME";
    private final String REPLACE_TABLE_NAME = "${tableName}";
    private String courseFile;
    /**
     *
     */
    private Configuration cfg;
    /**
     * 移除业务表的前缀
     */
    private static String[] removedTableNamePrefixes;
    /**
     * 生成文件类型,跟模板一一对应
     */
    private List<String> generalTypeList;

    public ModelGenerator() {
    }

    /**
     * 加载数据源
     */
    public static DataSource getDataSource() {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setUrl(jdbcUrl);
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        return dataSource;
    }

    /**
     * 初始化配置信息全局变量
     * @throws IOException
     */
    public void init() throws IOException {
        // TODO 数据库链接信息
        jdbcUrl = "jdbc:mysql://127.0.0.1:3306/xxlJob?characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull";
        username = "root";
        password = "root";
        // 去除表的业务前缀,此处以 xxl 业务表为例
        removedTableNamePrefixes = new String[]{"xxl_"};
        // 生成实体类、dao 文件、业务接口文件、业务实现类
        this.generalTypeList =
                Arrays.asList(GenerateTemplateEnum.GENERATE_MODEL.getCode(),
                        GenerateTemplateEnum.GENERATE_DAO.getCode(),
                        GenerateTemplateEnum.GENERATE_SERVICE.getCode(),
                        GenerateTemplateEnum.GENERATE_SERVICE_IMPL.getCode());
        this.courseFile = (new File(Global.EMPTY)).getCanonicalPath();
        String pathName =
                new StringBuilder(this.courseFile)
                        .append(Global.FREEMARKER_TEMPLATE_DIR)
                        .toString();
        this.cfg = new Configuration(Configuration.VERSION_2_3_30);
        this.cfg.setDirectoryForTemplateLoading(new File(pathName));
    }

    /**
     * @description: 生成模板类文件方法
     * @param: tableNames
     * @return: void
     * @author: niaonao
     * @date: 2021/7/13
     */
    public void generate(String... tableNames) {
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        PreparedStatement columnPs = null;
        ResultSet resultSet = null;
        Object columnRs = null;

        try {
            DataSource dataSource = getDataSource();
            connection = dataSource.getConnection();
            preparedStatement = connection.prepareStatement(SHOW_TABLE_NAME);
            if (tableNames.length == 0) {
                resultSet = preparedStatement.executeQuery();

                while(resultSet.next()) {
                    String tbName = resultSet.getString(1);
                    columnPs = connection.prepareStatement(SHOW_COLUMN_INFO.replace(REPLACE_TABLE_NAME, tbName));
                    this.generateTable(columnPs, tbName);
                }
            } else {
                String[] var22 = tableNames;
                int var9 = tableNames.length;

                for(int var10 = 0; var10 < var9; ++var10) {
                    String tbName = var22[var10];
                    columnPs = connection.prepareStatement(SHOW_COLUMN_INFO.replace(REPLACE_TABLE_NAME, tbName));
                    this.generateTable(columnPs, tbName);
                }
            }
        } catch (Exception var20) {
            System.out.println(var20.getMessage());
        } finally {
            try {
                if (resultSet != null) {
                    resultSet.close();
                }

                if (preparedStatement != null) {
                    preparedStatement.close();
                }

                if (columnRs != null) {
                    ((ResultSet)columnRs).close();
                }

                if (columnPs != null) {
                    columnPs.close();
                }

                if (connection != null) {
                    connection.close();
                }
            } catch (SQLException var19) {
                System.out.println(var19.getMessage());
            }

        }

    }

    /**
     * @description: 根据表设计及模板生成类文件
     * @param: columnPs
     * @param: tbName
     * @return: void
     * @author: niaonao
     * @date: 2021/7/13
     */
    private void generateTable(PreparedStatement columnPs, String tbName) {
        System.out.println("开始处理数据表:" + tbName);
        Map modelMap = new HashMap();
        ResultSet columnRs = null;

        try {
            String primaryKey = Global.EMPTY;
            Map<String, ColumnModel> colunms = new TreeMap();
            columnRs = columnPs.executeQuery();

            String generalType;
            String fileName;
            while(columnRs.next()) {
                String filed = columnRs.getString(1);
                String comment = columnRs.getString(2);
                generalType = columnRs.getString(3);
                String key = columnRs.getString(4);
                int endIndex = generalType.indexOf("(") > 0 ? generalType.indexOf("(") : generalType.length();
                fileName = DbDataTypeConvertUtil.getJavaType(generalType.substring(0, endIndex));
                if (!StringUtils.isEmpty(key) && "PRI".equals(key)) {
                    primaryKey = filed;
                }

                ColumnModel column = new ColumnModel();
                column.setColumnComment(comment);
                column.setColumnName(filed);
                column.setColumnType(fileName);
                colunms.put(filed, column);
            }

            TableModel em = new TableModel(tbName, primaryKey, toJavaClassStyle(tbName), colunms);
            modelMap.put("model", em);
            Iterator var17 = this.generalTypeList.iterator();

            while(var17.hasNext()) {
                generalType = (String)var17.next();
                Map<String, String> nameMap = this.getTemplateName(generalType, em.getClassName());
                String templateName = nameMap.get(TEMPLATE_NAME);
                fileName = nameMap.get(FILE_NAME);
                BufferedWriter modelBufferWriter = new BufferedWriter(new FileWriter(fileName));
                Template modelTemplate = this.cfg.getTemplate(templateName);
                modelTemplate.process(modelMap, modelBufferWriter);
            }

            System.out.println("业务路径下生成类文件完成,数据表:" + tbName);
        } catch (Exception var15) {
            System.out.println("业务路径下生成类文件异常, " + var15.getMessage());
        }

    }

    /**
     * @description: 获取模板及类文件生成路径
     * @param: modelType
     * @param: className
     * @return: java.util.Map<java.lang.String,java.lang.String>
     * @author: niaonao
     * @date: 2021/7/13
     */
    private Map<String, String> getTemplateName(String modelType, String className) {
        String templateFtl = Global.EMPTY;
        String fileDir = Global.EMPTY;
        GenerateTemplateEnum[] var5 = GenerateTemplateEnum.values();
        int var6 = var5.length;

        for(int var7 = 0; var7 < var6; ++var7) {
            GenerateTemplateEnum template = var5[var7];
            if (template.getCode().equals(modelType)) {
                templateFtl = template.getTemplateFtl();
                fileDir = this.getTargetDirByFtl(modelType, className);
                break;
            }
        }

        return ImmutableMap.of(TEMPLATE_NAME, templateFtl, FILE_NAME, fileDir);
    }

    /**
     * 根据业务层返回规范文件名称
     * @param modelType
     * @param className
     * @return generate fileName
     */
    private String getTargetDirByFtl(String modelType, String className) {
        StringBuilder targetDirBuilder = new StringBuilder(this.courseFile).append(Global.FREEMARKER_RELATIVE_DIR);
        if (GenerateTemplateEnum.GENERATE_DAO.getCode().equals(modelType)) {
            return targetDirBuilder
                    .append(GenerateTemplateEnum.GENERATE_DAO.getTargetDir())
                    .append(className)
                    .append("Dao.java")
                    .toString();
        }
        if (GenerateTemplateEnum.GENERATE_SERVICE.getCode().equals(modelType)) {
            return targetDirBuilder
                    .append(GenerateTemplateEnum.GENERATE_SERVICE.getTargetDir())
                    .append(className)
                    .append("Service.java")
                    .toString();
        }
        if (GenerateTemplateEnum.GENERATE_SERVICE_IMPL.getCode().equals(modelType)) {
            return targetDirBuilder
                    .append(GenerateTemplateEnum.GENERATE_SERVICE_IMPL.getTargetDir())
                    .append(className)
                    .append("ServiceImpl.java")
                    .toString();
        }

        // if (GenerateTemplateEnum.GENERATE_MODEL.getCode().equals(modelType)) {
        return targetDirBuilder
                .append(GenerateTemplateEnum.GENERATE_MODEL.getTargetDir())
                .append(className)
                .append(".java")
                .toString();
    }

    private static String toJavaClassStyle(String str) {
        if (removedTableNamePrefixes != null) {
            String[] var1 = removedTableNamePrefixes;
            int var2 = var1.length;

            for(int var3 = 0; var3 < var2; ++var3) {
                String prefix = var1[var3];
                if (str.startsWith(prefix)) {
                    str = str.replaceFirst(prefix, "");
                    break;
                }
            }
        }

        return underlineToCamelHump(str);
    }

    /**
     * @description: 下划线转驼峰
     * @param: str
     * @return: java.lang.String
     * @author: niaonao
     * @date: 2021/7/13
     */
    private static String underlineToCamelHump(String str) {
        Matcher matcher = Pattern.compile("_[a-z]").matcher(str);
        StringBuilder builder = new StringBuilder(str);

        for(int i = 0; matcher.find(); ++i) {
            builder.replace(matcher.start() - i, matcher.end() - i, matcher.group().substring(1).toUpperCase());
        }

        builder.replace(0, 1, String.valueOf(Character.toUpperCase(builder.charAt(0))));
        return builder.toString();
    }

    /**
     * @description: 根据表生成类文件
     * @param: tableNames
     * @return: void
     * @author: niaonao
     * @date: 2021/7/13
     */
    public static void autoGenerateModel(String... tableNames) {
        ModelGenerator generator = new ModelGenerator();

        try {
            generator.init();
            generator.generate(tableNames);
        } catch (Exception var3) {
            System.out.println(var3.getMessage());
        }

    }

    /*public static void main(String[] args) {
        autoGenerateModel("xxl_job_qrtz_trigger_group", "xxl_job_qrtz_trigger_info");
    }*/
}

    以表 xxl_job_qrtz_trigger_group、xxl_job_qrtz_trigger_info 为例,根据配置模板生成目标业务文件效果图如下。
在这里插入图片描述

图4.2、项目结构图

在这里插入图片描述

图4.3、目标文件生成图

5. 项目源码

    此处以生成目标类文件为例演示,实际开发中可用于各文本文件的生成,也不局限于后端类文件 .java 数据库脚本文件 .sql,比如前端资源 .html、.jsx、.md、.vue

    源码:https://gitee.com/niaonao/GenerateCodeTemplate
    文档:更多用法可参考 freemarker 中文文档,http://freemarker.foofun.cn/index.html

Powered By niaonao

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

niaonao

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值