Java + Freemarker 生成適合自己項目代碼模板

前言

     每個項目組甚至是一個公司的代碼風格幾乎都相同,其實每個需求下來,建完表之後,新建dao,service,和controller以及對應的實體類是一項非常簡單又麻煩的事情,一不小心寫錯單詞還要浪費半天時間去
找問題。通過固定的工具生成的代碼是不會有這樣的問題的。因此,在實際項目開發過程中,建立自己或者自己項目的代碼生成工具能提高開發效率降低代碼出錯的可能性。而java+Freemarker能根據項目的代碼設計
風格和格式很簡單的設計出最適合自己的代碼生成工具。
注意:本文只提供部分代碼,具體可直接運行項目請到本文最後連接地址直接下載
需要了解的技術和工具
java以及開發工具(本文用idea),freeMarker基本語法以及任意文檔開發工具可以直接用idea編寫(本文用VSCode開發工具),
模板原型:本文是設計基於mysql和JPA實現的dao層,以及通用的restful風格的controller接口和service層代碼,生成的代碼直接啓動springboot項目通過swagger訪問接口和數據庫交互
實現過程
假設mysql數據庫中有一個user表,表中字段名稱和類型以及說明如下
類型 長度
id bigint 20
creat_time timestamp 0
email varchar 100
image_url varchar 255
name varchar 100
password varchar 255
phone int 11
we_chat varchar 255

一、通過java代碼獲得數據庫中表的字段相關信息

關鍵代碼如下

public Connection getConnection() throws Exception {

        Class.forName(DRIVER);
        Connection connection = DriverManager.getConnection(URL, USER, PASSWORD);
        return connection;
    }
獲得數據庫連接之後,通過databaseMetaData對象傳入表名能獲得表中列相關的信息,表中列的信息放入到resultSet中
   DatabaseMetaData databaseMetaData = connection.getMetaData();
        ResultSet resultSet = databaseMetaData.getColumns(null, "%", tableName, "%");


 while (resultSet.next()) {
            columnClass = new ColumnClass();
            //獲取字段名稱
            columnClass.setColumnName(resultSet.getString("COLUMN_NAME"));
            //獲取字段類型
            columnClass.setColumnType(resultSet.getString("TYPE_NAME"));
            //轉換字段名稱,如 sys_name 變成 SysName            columnClass.setChangeColumnName(replaceUnderLineAndUpperCase(resultSet.getString("COLUMN_NAME")));
            //字段在數據庫的註釋
            columnClass.setColumnComment(resultSet.getString("REMARKS"));
            columnClassList.add(columnClass);

        }

二、封裝一個TemplateGenModel對象,該對象封裝了模板名稱、生成代碼的路徑,和要傳送的數據的map集合,以Entity爲例

   /**
     * 根據數據庫對象創建java實體類對象
     *
     * @param resultSet 查詢數據庫返回對象
     * @throws Exception 拋出異常
     */


    public static TemplateGenModel generateEntityFile(ResultSet resultSet, String diskPath, String changeTableName) throws Exception {


        final String suffix = ".java";
        final String pagePath = diskPath + File.separator+"entity"+File.separator;
        File pageFile = new File(pagePath);
        if (!pageFile.exists()) {
            pageFile.mkdirs();
        }
        final String path = diskPath + File.separator+"entity"+File.separator + changeTableName + suffix;
        final String templateName = "Entity.ftl";
        File mapperFile = new File(path);
        List<ColumnClass> columnClassList = new ArrayList<>();
        ColumnClass columnClass = null;
        while (resultSet.next()) {
            columnClass = new ColumnClass();
            String length = resultSet.getString("CHAR_OCTET_LENGTH");
            //獲取字段名稱
            columnClass.setColumnName(resultSet.getString("COLUMN_NAME"));
            //獲取字段類型
            columnClass.setColumnType(resultSet.getString("TYPE_NAME"));
            //轉換字段名稱,如 sys_name 變成 SysName
            columnClass.setChangeColumnName(replaceUnderLineAndUpperCase(resultSet.getString("COLUMN_NAME")));
            //字段在數據庫的註釋
            columnClass.setColumnComment(resultSet.getString("REMARKS"));
            if (length!=null){
                columnClass.setCharOctetLength(length);
            }
            columnClassList.add(columnClass);
        }
        Map<String, Object> dataMap = new HashMap<>();
        dataMap.put("model_column", columnClassList);
        return new TemplateGenModel(templateName, mapperFile, dataMap);
    }
public class ColumnClass {

        /** 數據庫字段名稱 **/
        private String columnName;
        /** 數據庫字段類型 **/
        private String columnType;
        /** 數據庫字段首字母小寫且去掉下劃線字符串 **/
        private String changeColumnName;
        /** 數據庫字段註釋 **/
        private String columnComment;


        private String charOctetLength;
}

三、根據模板名稱獲得freemarker.template.Template對象,具體代碼如下:

public class FreeMarkerTemplateUtils {
    private FreeMarkerTemplateUtils(){}
    private static final Configuration CONFIGURATION = new Configuration(Configuration.VERSION_2_3_22);


    static{
        //這裏用來指定模板所在的路徑,本項目配置在resources/templates目錄下,springBoot項目會默認到resources下讀文件
        CONFIGURATION.setTemplateLoader(new ClassTemplateLoader(FreeMarkerTemplateUtils.class, "/templates"));
        CONFIGURATION.setDefaultEncoding("UTF-8");
        CONFIGURATION.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
        CONFIGURATION.setCacheStorage(NullCacheStorage.INSTANCE);
    }


    public static Template getTemplate(String templateName) throws IOException {
        try {
            return CONFIGURATION.getTemplate(templateName);
        } catch (IOException e) {
            throw e;
        }
    }
}
通過我們前面封裝的TemplateGenModel對象中的文件路徑,生成文件輸入流fos
FileOutputStream fos = new FileOutputStream(genModel.getMapperFile());
並且需一些公用字段在這裏再次封裝到TemplateGenModel對象中
   genModel.getDataMap().put("table_name_small", tableName);
        genModel.getDataMap().put("table_name", changeTableName);
        genModel.getDataMap().put("author", AUTHOR);
        genModel.getDataMap().put("date", CURRENT_DATE);
        genModel.getDataMap().put("package_name", packageName);
        genModel.getDataMap().put("table_annotation", tableAnnotation);
        genModel.getDataMap().put("business_package_name",businessBasePackage);
        genModel.getDataMap().put("id_type", idType);
把字節流封裝成字符輸出了:
Writer out = new BufferedWriter(new OutputStreamWriter(fos, "utf-8"), 10240);
通過freemarker.template.Template對象的process方法傳入需要傳送的數據和字符流,通過模板生成相應的.java文件。其實生成代碼就是生成到指定目錄下的文件。
Writer out = new BufferedWriter(new OutputStreamWriter(fos, "utf-8"), 10240);
    template.process(genModel.getDataMap(), out);

四、獲得相關屬性之後把獲取到的值放到freeMark模板中,entity模板如下:

package ${business_package_name}.entity;


import javax.persistence.*;


import lombok.Data;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;


<#if model_column?exists>
    <#list model_column as model>
        <#if (model.columnType?lower_case = 'datetime' || model.columnType?lower_case = 'date' || model.columnType?lower_case = 'time' || model.columnType?lower_case = 'year' || model.columnType?lower_case = 'timestamp')>
import java.util.Date;


        </#if>
        <#if (model.columnType?lower_case = 'decimal')>
import java.math.BigDecimal;


        </#if>
    </#list>
</#if>
/**
 * 描述:${table_annotation}模型
 *
 * @author ${author}
 * @date ${date}
 */
@Entity
@Table(name = "${table_name_small}")
@EntityListeners(AuditingEntityListener.class)
<#--  @Where(clause = "status > '0'")  -->
<#--  @Inheritance(strategy= InheritanceType.SINGLE_TABLE)  -->
@Data
public class ${table_name} {


<#if model_column?exists>
    <#list model_column as model>
    /**
     * ${model.columnComment!}
     */
        <#if (model.columnName?lower_case = 'id')>
        <#if (model.columnType?lower_case = 'bigint')>
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "${model.columnName}", columnDefinition = "${model.columnType}")
    private Long ${model.changeColumnName?uncap_first};
        <#else >
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "${model.columnName}", columnDefinition = "${model.columnType}")
    private Integer ${model.changeColumnName?uncap_first};
        </#if>
        <#else>
        <#if (model.columnType?lower_case = 'tinyint' || model.columnType?lower_case = 'bit')>
    @Column(name = "${model.columnName}", columnDefinition = "VARCHAR")
    private Boolean ${model.changeColumnName?uncap_first};
        <#elseif (model.columnType?lower_case = 'smallint' || model.columnType?lower_case = 'mediumint' || model.columnType?lower_case = 'int' || model.columnType?lower_case = 'integer')>
    @Column(name = "${model.columnName}", columnDefinition = "${model.columnType}")
    private Integer ${model.changeColumnName?uncap_first};
        <#elseif (model.columnType?lower_case = 'bigint')>
    @Column(name = "${model.columnName}", columnDefinition = "${model.columnType}")
    private Long ${model.changeColumnName?uncap_first};
        <#elseif (model.columnType?lower_case = 'float')>
    @Column(name = "${model.columnName}", columnDefinition = "${model.columnType}")
    private Float ${model.changeColumnName?uncap_first};
        <#elseif (model.columnType?lower_case = 'double')>
    @Column(name = "${model.columnName}", columnDefinition = "${model.columnType}")
    private Double ${model.changeColumnName?uncap_first};
        <#elseif (model.columnType?lower_case = 'decimal')>
    @Column(name = "${model.columnName}", columnDefinition = "${model.columnType}")
    private Decimal ${model.changeColumnName?uncap_first};
        <#elseif (model.columnType?lower_case = 'varchar' || model.columnType?lower_case = 'text')>
    @Column(name = "${model.columnName}", columnDefinition = "VARCHAR(${model.charOctetLength})")
    private String ${model.changeColumnName?uncap_first};
        <#elseif model.columnType?lower_case = 'datetime' || model.columnType?lower_case = 'date' || model.columnType?lower_case = 'time' || model.columnType?lower_case = 'year' || model.columnType?lower_case = 'timestamp'>
    @Column(name = "${model.columnName}", columnDefinition = "TIMESTAMP")
    private Date ${model.changeColumnName?uncap_first};
        <#else>
        </#if>
        </#if>
    </#list>
</#if>
}
五、生成的代碼如下,如Entity
/**
 * 描述:user模型
 *
 * @author why
 * @date 2018-06-25 14:17:51
 */
@Entity
@Table(name = "user")
@EntityListeners(AuditingEntityListener.class)
@Data
public class User {


    /**
     * 
     */
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id", columnDefinition = "BIGINT")
    private Long id;
}


查看完整項目代碼請下載鏈接:https://pan.baidu.com/s/1o-eAg-xhpyN28sg-jeie9g 密碼:e0ze
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章