FreeMarker生成java類與sql文件


  在javaweb開發過程中往往需要創建很多與數據庫關聯的java實體類與sql(xml格式)文件,這些工作是既耗時,又沒什么技術含量,但是卻是必不可少的工作。使用過mybatis的都知道,mybatis有對應的插件可以快速生成相應的java類與sql文件,通過使用插件可以大大提高開發效率。但是並不是所有的公司都使用mybatis,本人所在的公司就是其中之一。本人所在公司雖然使用的不是mybatis,但是開發過程卻是很像,都需要創建實體類與sql文件。由於每次開發都需要創建大量的實體類與sql文件,使本人開發時感到很痛苦,尤其數據庫表字段特別多的時候真是相當的痛苦。基於以上原因,本人研究了一下使用FreeMarker 生成文件的方法,在此記錄一下,也方便以后的查找。

 “FreeMarker是一款 模板引擎: 即一種基於模板和要改變的數據, 並用來生成輸出文本(HTML網頁,電子郵件,配置文件,源代碼等)的通用工具。 它不是面向最終用戶的,而是一個Java類庫,是一款程序員可以嵌入他們所開發產品的組件。模板編寫為FreeMarker Template Language (FTL)。它是簡單的,專用的語言, 不是 像PHP那樣成熟的編程語言。 那就意味着要准備數據在真實編程語言中來顯示,比如數據庫查詢和業務運算, 之后模板顯示已經准備好的數據。在模板中,你可以專注於如何展現數據, 而在模板之外可以專注於要展示什么數據。”--來源於網絡

  FreeMarker是一種模板引擎,使用過springmvc的應該都知道,springmvc支持多種模板引擎,其中FreeMarker就是其中之一。當然,在springmvc中主要用來做作為前端頁面使用。要使用FreeMarker,首先要有對應的模板,其模板的格式為*.ftl。行了,說了這么多,好像都是廢話,下面直接上代碼。

  1、ftl模板

 

 

   此處主要有兩個模板:bean.ftl和sqlMap.ftl,一個用來生成實體類,一個用來生成sql文件。

  bean.ftl

package ${packageName};

import com.alibaba.fastjson.JSONObject;
import java.util.Date;

/**
* 〈一句話功能簡述〉
* 〈功能詳細描述〉
*
* @author 19043197
* @see [相關類/方法](可選)
* @since [產品/模塊版本] (可選)
*/
public class ${className} implements Serializable {

    private static final long serialVersionUID = 1L;

<#-- 循環類型及屬性 -->
<#list attrs as attr>
    //${attr.remarks}
    private ${attr.type} ${attr.name};

</#list>

<#-- 循環生成set get方法 -->
<#list attrs as attr>
    public void set${attr.firstUpperName}(${attr.type} ${attr.name}) {
        this.${attr.name} = ${attr.name};
    }

    public ${attr.type} get${attr.firstUpperName}() {
        return ${attr.name};
    }

</#list>

    @Override
    public String toString() {
        return JSONObject.toJSONString(this);
    }
}

 

  sqlMap.ftl

package ${packageName};

import com.alibaba.fastjson.JSONObject;
import java.util.Date;

/**
* 〈一句話功能簡述〉
* 〈功能詳細描述〉
*
* @author 19043197
* @see [相關類/方法](可選)
* @since [產品/模塊版本] (可選)
*/
public class ${className} implements Serializable {

    private static final long serialVersionUID = 1L;

<#-- 循環類型及屬性 -->
<#list attrs as attr>
    //${attr.remarks}
    private ${attr.type} ${attr.name};

</#list>

<#-- 循環生成set get方法 -->
<#list attrs as attr>
    public void set${attr.firstUpperName}(${attr.type} ${attr.name}) {
        this.${attr.name} = ${attr.name};
    }

    public ${attr.type} get${attr.firstUpperName}() {
        return ${attr.name};
    }

</#list>

    @Override
    public String toString() {
        return JSONObject.toJSONString(this);
    }
}

 

  簡單介紹下FreeMarker語法:

    (1)、${className} 直接獲取對象屬性

 (2)、<#list attrs as attr></#list> 對集合進行循環

 (3)、<#if attr_has_next></#if> 條件判斷,attr_has_next判斷還有下一個,格式:對象_has_next

 (4)、${r" "}或${r''}特殊字符顯示,由於本人所在公司sql(xml格式)文件使用的freemerker解析的,所以生成的文件中難免會有以下Freemarker的標簽

    2、數據庫連接類(此處只貼了mysql的)

package utils;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

/**
 *  數據庫連接工具類(僅mysql)
 * 〈功能詳細描述〉
 *
 * @author lipan
 * @see [相關類/方法](可選)
 * @since [產品/模塊版本] (可選)
 */
public class MysqlDBUtil {

    private static String URL;
    private static String USER;
    private static String PASSWORD;
    private static String DRIVER;
    static{
        URL = "jdbc:mysql://localhost:3306/lpan?useSSL=false&serverTimezone=UTC";
        USER = "root";
        PASSWORD = "1234";
        DRIVER = "com.mysql.cj.jdbc.Driver";
    }

    public static Connection getConnection() throws Exception{
        Class.forName(DRIVER);
        Connection connection= DriverManager.getConnection(URL, USER, PASSWORD);
        return connection;
    }

    public static void close(Connection connection){
        if(connection!=null){
            try {
                connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

    3、ColumnBean類

  主要是定義數據字段與實體類屬性

package utils;

/**
 *  實體類
 * 〈功能詳細描述〉
 *
 * @author [作者](必須)
 * @see [相關類/方法](可選)
 * @since [產品/模塊版本] (可選)
 */
public class ColumnBean {

    //成員變量類型
    private String type;
    //成員變量名稱
    private String name;
    //注釋
    private String remarks;
    //首字母大寫
    private String firstUpperName;
    //字段(大寫)
    private String columnName;
    //指定長度的字段大寫,不足右側補空格(select sql使用,美觀,個人習慣)
    private String columnNameWithLen;

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getRemarks() {
        return remarks;
    }

    public void setRemarks(String remarks) {
        this.remarks = remarks;
    }

    public String getFirstUpperName() {
        return firstUpperName;
    }

    public void setFirstUpperName(String firstUpperName) {
        this.firstUpperName = firstUpperName;
    }

    public String getColumnName() {
        return columnName;
    }

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

    public String getColumnNameWithLen() {
        return columnNameWithLen;
    }

    public void setColumnNameWithLen(String columnNameWithLen) {
        this.columnNameWithLen = columnNameWithLen;
    }

    public ColumnBean(String type, String name, String remarks, String firstUpperName, String columnName, String columnNameWithLen) {
        this.type = type;
        this.name = name;
        this.remarks = remarks;
        this.firstUpperName = firstUpperName;
        this.columnName = columnName;
        this.columnNameWithLen = columnNameWithLen;
    }

    public ColumnBean() {
    }
}

    4、從數據庫查詢表字段信息封裝成ColumnBean(此處有mysql和postgresql兩種)

package utils;

import com.alibaba.fastjson.JSONObject;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 *  數據處理
 * 〈功能詳細描述〉
 *
 * @author [作者](必須)
 * @see [相關類/方法](可選)
 * @since [產品/模塊版本] (可選)
 */
public class DataUtils {

    public static String dealDataTypeForPG(String dataType){
        if(dataType==null || dataType.length()==0){
            throw new RuntimeException("字段類型為空");
        }
        if(dataType.contains("character varying")){
            return "String";
        }else if(dataType.contains("numeric")){
            return "BigDecimal";
        }else if(dataType.contains("timestamp")){
            return "Date";
        }else if(dataType.contains("int4")){
            return "Integer";
        }
        throw new RuntimeException("字段類型轉換異常");
    }

    public static String dealDataTypeForMysql(String dataType){
        if(dataType==null || dataType.length()==0){
            throw new RuntimeException("字段類型為空");
        }
        if(dataType.contains("varchar")){
            return "String";
        }else if(dataType.contains("decimal")){
            return "BigDecimal";
        }else if(dataType.contains("datetime")){
            return "Date";
        }else if(dataType.contains("int")){
            return "Integer";
        }
        throw new RuntimeException("字段類型轉換異常");
    }

    /**
     * 首字母大寫
     * @param columnName
     * @return
     */
    public static String dealColumnNameFirstUpper(String columnName){
        return columnName.substring(0,1)+columnName.substring(1);
    }

    private static Pattern linePattern = Pattern.compile("_(\\w)");

    /** 下划線轉駝峰 */
    public static String lineToHump(String str) {
        str = str.toLowerCase();
        Matcher matcher = linePattern.matcher(str);
        StringBuffer sb = new StringBuffer();
        while (matcher.find()) {
            matcher.appendReplacement(sb, matcher.group(1).toUpperCase());
        }
        matcher.appendTail(sb);
        return sb.toString();
    }

    public static ColumnBean createColumnBeanName(String dataType,String columnName,String remarks,String dbType){
        String type = "";
        if("mysql".equals(dbType)){
            type = dealDataTypeForMysql(dataType);
        }else{
            type = dealDataTypeForPG(dataType);
        }
        String name = lineToHump(columnName);
        String firstUpperName = dealColumnNameFirstUpper(name);
        String newColumnName = addEmptyChar(columnName.toUpperCase(), 25);
        ColumnBean columnBean = new ColumnBean(type,name,remarks,firstUpperName,columnName.toUpperCase(),newColumnName);
        return columnBean;
    }

    /**
     * 字符串不足指定長度補空格
     * @param str
     * @param length
     * @return
     */
    public static String addEmptyChar(String str,int length){
        StringBuffer buffer = new StringBuffer();
        if(str.isEmpty()){
            for(int i=0;i<length;i++){
                buffer.append(" ");
            }
        }else if(str.length()<length){
            buffer.append(str);
            for(int i=0;i<length-str.length();i++){
                buffer.append(" ");
            }
        }else{
            buffer.append(str);
        }
        return buffer.toString();
    }

    public static List<ColumnBean> processDataForPG(Connection connection, String tableName) throws SQLException {
        StringBuffer sql = new StringBuffer();
        sql.append(" SELECT  ");
        sql.append("     b.attname AS column_name, ");
        sql.append("     c.description AS remarks, ");
        sql.append("     pg_catalog.format_type(b.atttypid,b.atttypmod) AS data_type ");
        sql.append(" FROM ");
        sql.append("     pg_catalog.pg_class a, ");
        sql.append("     pg_catalog.pg_attribute b, ");
        sql.append("     pg_catalog.pg_description c ");
        sql.append(" WHERE ");
        sql.append("     a.oid=b.attrelid ");
        sql.append(" AND b.attrelid=c.objoid ");
        sql.append(" AND a.relname= ? ");
        sql.append(" AND c.objsubid=b.attnum ");
        PreparedStatement preparedStatement = connection.prepareStatement(sql.toString());
        preparedStatement.setString(1,tableName);
        System.out.println(preparedStatement.toString());
        ResultSet resultSet = preparedStatement.executeQuery();
        List<ColumnBean> list = new ArrayList<ColumnBean>();
        while (resultSet.next()){
            String dataType = resultSet.getString("data_type");
            String columnName = resultSet.getString("column_name");
            String remarks = resultSet.getString("remarks");
            ColumnBean columnBean = createColumnBeanName(dataType, columnName, remarks,"PG");
            list.add(columnBean);
        }
        return list;
    }

    public static List<ColumnBean> processDataForMysql(Connection connection, String tableName) throws SQLException {
        StringBuffer sql = new StringBuffer();
        sql.append(" select * from information_schema.COLUMNS where table_name= ? ");
        PreparedStatement preparedStatement = connection.prepareStatement(sql.toString());
        preparedStatement.setString(1,tableName);
        System.out.println(preparedStatement.toString());
        ResultSet resultSet = preparedStatement.executeQuery();
        List<ColumnBean> list = new ArrayList<ColumnBean>();
        while (resultSet.next()){
            String dataType = resultSet.getString("data_type");
            String columnName = resultSet.getString("column_name");
            String remarks = resultSet.getString("column_comment");
            ColumnBean columnBean = createColumnBeanName(dataType, columnName, remarks,"mysql");
            list.add(columnBean);
        }
        return list;
    }
}

    5、使用FreeMarker生成具體文件

package utils;

import freemarker.template.Configuration;
import freemarker.template.Template;

import java.io.*;
import java.sql.Connection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 *  模板生成類
 * 〈功能詳細描述〉
 *
 * @author [作者](必須)
 * @see [相關類/方法](可選)
 * @since [產品/模塊版本] (可選)
 */
public class FreemarkerGenerator {
    //模板路徑
    public static String PATH = "D:\\IdeaProjects\\freemarker-generator\\src\\main\\resources";
    //生成文件路徑
    public static String OUT_PATH = "E:\\study\\";

    public static void createBean(Connection connection,String packageName, String className, String tableName,String dbType){
        // step1 創建freeMarker配置實例
        Configuration configuration = new Configuration(Configuration.VERSION_2_3_0);
        Writer out = null;
        try {
            // step2 獲取模版路徑
            configuration.setDirectoryForTemplateLoading(new File(PATH));
            // step3 創建數據模型
            Map<String, Object> dataMap = new HashMap<String, Object>();
            List<ColumnBean> columnBeans = null;
            if("mysql".equals(dbType)){
                columnBeans = DataUtils.processDataForMysql(connection,tableName);
            }else{
                columnBeans = DataUtils.processDataForPG(connection,tableName);
            }
            dataMap.put("packageName", packageName);
            dataMap.put("className", className);
            dataMap.put("attrs",columnBeans);
            // step4 加載模版文件
            Template template = configuration.getTemplate("bean.ftl");
            // step5 生成數據
            File docFile = new File(OUT_PATH+className+".java");
            out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(docFile)));
            // step6 輸出文件
            template.process(dataMap, out);
            System.out.println("文件創建成功 !");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (null != out) {
                    out.flush();
                }
            } catch (Exception e2) {
                e2.printStackTrace();
            }
        }
    }

    public static void createSqlMap(Connection connection,String tableName,String fileName,String namespace,String dbType){
        // step1 創建freeMarker配置實例
        Configuration configuration = new Configuration(Configuration.VERSION_2_3_0);
        Writer out = null;
        try {
            // step2 獲取模版路徑
            configuration.setDirectoryForTemplateLoading(new File(PATH));
            // step3 創建數據模型
            Map<String, Object> dataMap = new HashMap<String, Object>();

            List<ColumnBean> columnBeans = null;
            if("mysql".equals(dbType)){
                columnBeans = DataUtils.processDataForMysql(connection,tableName);
            }else{
                columnBeans = DataUtils.processDataForPG(connection,tableName);
            }
            dataMap.put("tableName", tableName);
            dataMap.put("namespace", namespace);
            dataMap.put("pageSize","${pageSize}");
            dataMap.put("startPage","${startPage}");
            dataMap.put("attrs",columnBeans);
            // step4 加載模版文件
            Template template = configuration.getTemplate("sqlMap.ftl");
            // step5 生成數據
            File docFile = new File(OUT_PATH+fileName+".xml");
            out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(docFile)));
            // step6 輸出文件
            template.process(dataMap, out);
            System.out.println("文件創建成功 !");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (null != out) {
                    out.flush();
                }
            } catch (Exception e2) {
                e2.printStackTrace();
            }
        }
    }

}
PATH 模板的路徑
OUT_PATH生成文件的路徑
代碼注釋應該比較清楚,在此不多做解釋。
6、測試類
package utils;

import com.alibaba.fastjson.JSONObject;
import freemarker.template.Configuration;
import freemarker.template.Template;
import org.junit.Test;

import java.io.*;
import java.sql.Connection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 *  測試類
 * 〈功能詳細描述〉
 *
 * @author [作者](必須)
 * @see [相關類/方法](可選)
 * @since [產品/模塊版本] (可選)
 */
public class FreemarkerGeneratorTest {

    @Test
    public void testKeyWord() {
        // step1 創建freeMarker配置實例
        Configuration configuration = new Configuration(Configuration.VERSION_2_3_0);
        Writer out = null;
        try {
            // step2 獲取模版路徑
            configuration.setDirectoryForTemplateLoading(new File(FreemarkerGenerator.PATH));
            // step3 創建數據模型
            Map<String, Object> dataMap = new HashMap<String, Object>();
            dataMap.put("aa", "${pageSize}");
            // step4 加載模版文件
            Template template = configuration.getTemplate("test.ftl");
            // step5 生成數據
            File docFile = new File(FreemarkerGenerator.OUT_PATH + "aa.txt");
            out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(docFile)));
            // step6 輸出文件
            template.process(dataMap, out);
            System.out.println("^^^^^^^^^^^^^^^^^^^^^^^^文件創建成功 !");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (null != out) {
                    out.flush();
                }
            } catch (Exception e2) {
                e2.printStackTrace();
            }
        }
    }


    @Test
    public void processDataForMysqlTest() throws Exception {
        Connection connection = MysqlDBUtil.getConnection();
        List<ColumnBean> list = DataUtils.processDataForMysql(connection, "d_menu");
        JSONObject.toJSONString(list);
        System.out.println(JSONObject.toJSONString(list));
        MysqlDBUtil.close(connection);
    }

    @Test
    public void createBeanWithMysql() throws Exception {
        String packageName = "com.test";
        String className = "Menu";
        String tableName = "d_menu";
        Connection connection = MysqlDBUtil.getConnection();
        FreemarkerGenerator.createBean(connection,packageName,className,tableName,"mysql");
        PGDBUtil.close(connection);
    }

    @Test
    public void createSqlMapWithMysql() throws Exception {
        String tableName = "d_menu";
        String fileName = "sqlMap_menu";
        String namespace = "menu";
        Connection connection = MysqlDBUtil.getConnection();
        FreemarkerGenerator.createSqlMap(connection,tableName,fileName,namespace,"mysql");
        PGDBUtil.close(connection);
    }
}
createBeanWithMysql和createSqlMapWithMysql分別是測試生成實體類和sql配置文件。
生成后的文件

Menu.java

package com.test;

import com.alibaba.fastjson.JSONObject;

/**
* 〈一句話功能簡述〉
* 〈功能詳細描述〉
*
* @author xxxxx
* @see [相關類/方法](可選)
* @since [產品/模塊版本] (可選)
*/
public class Menu implements Serializable {

    private static final long serialVersionUID = -5717628049858495391L;

    //主鍵
    private Integer menuId;

    //創建時間
    private Date createDate;

    //圖標
    private String iconCls;

    //菜單名稱
    private String menuName;

    //父id
    private Integer pid;

    //狀態
    private String status;

    //菜單地址
    private String url;


    public void setmenuId(Integer menuId) {
        this.menuId = menuId;
    }

    public Integer getmenuId() {
        return menuId;
    }

    public void setcreateDate(Date createDate) {
        this.createDate = createDate;
    }

    public Date getcreateDate() {
        return createDate;
    }

    public void seticonCls(String iconCls) {
        this.iconCls = iconCls;
    }

    public String geticonCls() {
        return iconCls;
    }

    public void setmenuName(String menuName) {
        this.menuName = menuName;
    }

    public String getmenuName() {
        return menuName;
    }

    public void setpid(Integer pid) {
        this.pid = pid;
    }

    public Integer getpid() {
        return pid;
    }

    public void setstatus(String status) {
        this.status = status;
    }

    public String getstatus() {
        return status;
    }

    public void seturl(String url) {
        this.url = url;
    }

    public String geturl() {
        return url;
    }


    @Override
    public String toString() {
        return JSONObject.toJSONString(this);
    }
}

sqlMap_menu.xml

<?xml version="1.0" encoding="UTF-8" ?>
<sqlMap namespace="menu">

    <sql id="queryCount">
        <![CDATA[
            SELECT
                COUNT(ID)    AS    TOTALCOUNT
            FROM
                D_MENU
            WHERE 1=1
        ]]>
    </sql>

    <sql id="queryList">
        <![CDATA[
            SELECT
                MENU_ID                   AS "menuId",
                CREATE_DATE               AS "createDate",
                ICON_CLS                  AS "iconCls",
                MENU_NAME                 AS "menuName",
                PID                       AS "pid",
                STATUS                    AS "status",
                URL                       AS "url"
            FROM
                D_MENU
            WHERE 1=1
                LIMIT ${pageSize} OFFSET ${startPage}
        ]]>
    </sql>

    <sql id="insert">
        <![CDATA[
            INSERT INTO D_MENU(
                MENU_ID,
                CREATE_DATE,
                ICON_CLS,
                MENU_NAME,
                PID,
                STATUS,
                URL
            )
            VALUES
            (
                :menuId,
                :createDate,
                :iconCls,
                :menuName,
                :pid,
                :status,
                :url
            )
        ]]>
    </sql>

    <sql id="update">
        <![CDATA[
            UPDATE D_MENU
            SET
                MENU_ID = :menuId,
                CREATE_DATE = :createDate,
                ICON_CLS = :iconCls,
                MENU_NAME = :menuName,
                PID = :pid,
                STATUS = :status,
                URL = :url
            WHERE
            <#if menuId?exists && menuId!=''>
                    MENU_ID = :menuId
            </#if>
            <#if createDate?exists && createDate!=''>
                AND CREATE_DATE = :createDate
            </#if>
            <#if iconCls?exists && iconCls!=''>
                AND ICON_CLS = :iconCls
            </#if>
            <#if menuName?exists && menuName!=''>
                AND MENU_NAME = :menuName
            </#if>
            <#if pid?exists && pid!=''>
                AND PID = :pid
            </#if>
            <#if status?exists && status!=''>
                AND STATUS = :status
            </#if>
            <#if url?exists && url!=''>
                AND URL = :url
            </#if>
        ]]>
    </sql>


</sqlMap>

 

 github地址:https://github.com/panli1988/freemarker-generator

    以上全部內容,僅供參考


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM