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

    以上全部内容,仅供参考

posted @ 2020-04-04 10:37  小小的暗影  阅读(2097)  评论(0编辑  收藏  举报