Freemarker简单实现一个代码生成器。

忽然想起以前工作的时候突发奇想写的一个一个代码生成器,也记录一下吧。

首先文件如下:

Freemarker简单实现一个代码生成器。

我的模板配置如下:

package ${packageName};
import java.util.Date;
public class ${className}{

<#-- 循环类型及属性 -->
<#list attrs as attr>
     private ${attr.type} ${attr.name}; //${attr.remarks}
</#list>

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

Freemarker加载类:

package cn.itrip.beans.util;


import java.io.File;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateExceptionHandler;

public class FreeMarkerInit {

    private static FreeMarkerInit single= new FreeMarkerInit();
    private FreeMarkerInit() {}
    //静态工厂方法
    public static FreeMarkerInit getInstance() {
        return single;
    }

    public Template getDefinedTemplate(String templateName) throws Exception{
        //配置类
        Configuration cfg = new Configuration(Configuration.VERSION_2_3_22);
        cfg.setDirectoryForTemplateLoading(new File("E:/springBootLearn/itrip-test/itripbeans/src/main/resources/template/"));
        cfg.setDefaultEncoding("UTF-8");
        cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
        return cfg.getTemplate(templateName);
    }
}

 

Freemarker提供了3种加载模板目录的方法。 它使用Configuration类加载模板。

三种方法分别是:

public void setClassForTemplateLoading(Class clazz, String pathPrefix);

public void setDirectoryForTemplateLoading(File dir) throws IOException;

public void setServletContextForTemplateLoading(Object servletContext, String path);

 

第一种:基于类路径,HttpWeb包下的framemaker.ftl文件
  configuration.setClassForTemplateLoading(this.getClass(), "/HttpWeb");

configuration.getTemplate("framemaker.ftl"); //framemaker.ftl为要装载的模板 
第二种:基于文件系统

configuration.setDirectoryForTemplateLoading(new File("/template"))

configuration.getTemplate("framemaker.ftl"); //framemaker.ftl为要装载的模板

第三种:基于Servlet Context,指的是基于WebRoot下的template下的framemaker.ftl文件

HttpServletRequest request = ServletActionContext.getRequest();

configuration.setServletContextForTemplateLoading(request.getSession().getServletContext(), "/template");

configuration.getTemplate("framemaker.ftl"); //framemaker.ftl为要装载的模板

 

连接数据库获取元信息,得到表名,列信息。

 

package cn.itrip.beans.util;

import java.sql.*;
import java.util.ArrayList;
import java.util.List;

/**
 * 负责获取表的元信息
 * 列的元信息
 */

public class MetadataUtil {
    private static Connection conn;
    private static DatabaseMetaData meta;

    static {
        try {
            Class.forName("com.mysql.jdbc.Driver");
        }catch (ClassNotFoundException e){
            e.printStackTrace();
            System.out.println("数据库连接失败!");
        }
    }
    public static void openConnection(){
        try {
            if (conn==null||conn.isClosed()){
                conn=DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/itripdb",
                        "root","root");
                meta=conn.getMetaData();
            }
        }catch (SQLException e){
            e.printStackTrace();
        }
    }
    //获取注解
    public static  String getCommentByTableName(String tableName) throws Exception{
        openConnection();
        Statement stmt=conn.createStatement();
        ResultSet rs=stmt.executeQuery("SHOW CREATE TABLE "+tableName);
        String comment=null;
        if (rs!=null&&rs.next()){
            comment=rs.getString(2);
        }
        rs.close();
        stmt.close();
        conn.close();
        return comment;
    }

    //获取所有表名称
    public static List<String> getTableNames(){
        openConnection();
        ResultSet rs=null;
        List<String> nameList=new ArrayList<>();
        try {
            rs=meta.getTables(null,null,null,new String[]{"TABLE"});
            while (rs.next()){
                String tName=rs.getString("TABLE_NAME");
                nameList.add(tName);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        return  nameList;
    }

    /**
     * 列信息数组的集合。List中每个元素是一个数组,代表一个列的信息;
     * 每个数组的元素1是列名,元素2是注释,元素3是类型
     * @return
     */
    public static List<String[]> getTableColumnsInfo(String tableName) throws  Exception{
        openConnection();
        ResultSet rs=meta.getColumns(null,"%",tableName,"%");
        List<String[]> columnInfoList=new ArrayList<>();
        while (rs.next()){
            String[] colInfo=new  String[3];
            colInfo[0] =rs.getString("COLUMN_NAME");
            colInfo[1] =rs.getString("REMARKS");
            colInfo[2] =rs.getString("TYPE_NAME");
            columnInfoList.add(colInfo);
        }
        return columnInfoList;
    }


    public static DatabaseMetaData getMeta() {
        return meta;
    }

    public static void setMeta(DatabaseMetaData meta) {
        MetadataUtil.meta = meta;
    }

    public static Connection getConn() {
        return conn;
    }

    public static void setConn(Connection conn) {
        MetadataUtil.conn = conn;
    }
}

 

直接获取的表名,列信息有时候并不满足需求规范所有我们Java规范或者项目需求,所以我又编写了一个元信息转换工具类。

package cn.itrip.beans.util;



/**
 * java类名、属性名、方法名转换工具类
 *
 * @author imagines
 */
public class JavaNameUtil {

    /**
     * 将数据库(表、字段)转换以java命名方式帕斯卡或者骆驼
     * @param unberscoreName
     * @param isPascal       是否将首字母转化大写,true则转化为骆驼命名,false则转换为帕斯卡命名
     * @return 骆驼或帕斯卡命名字符串
     */
    public static String translate(String unberscoreName, boolean isPascal) {
        StringBuilder result = new StringBuilder();
        //从第一个字母
        if (unberscoreName != null && unberscoreName.length() !=0) {
            boolean flag = false;
            char firstChar = unberscoreName.charAt(0);  //得到首字母
            if (isPascal) {
                result.append(Character.toUpperCase(firstChar));
            } else {
                result.append(firstChar);
            }
            //从第二个字母以后开始
            for (int i = 1, length = unberscoreName.length(); i < length; i++) {
                char ch = unberscoreName.charAt(i);
                if ('_' == ch) {
                    flag = true;
                } else {
                    if (flag) { //标记上一个是下划线,就转化为大写。
                        result.append(Character.toUpperCase(ch));
                        flag = false;
                    } else {
                        result.append(ch);
                    }
                }
            }
        }
        return result.toString();
    }

    /**
     * 调用translate() 转换为帕斯卡命名
     * @param unberscoreName 数据库(表名、字段名)
     * @return
     */
    public static String toPascal(String unberscoreName) {
        return translate(unberscoreName, true);
    }

    /**
     * 调用translate() 转换为骆驼命名
     * @param unberscoreName 数据库(表名、字段名)
     * @return
     */
    public static String toCamel(String unberscoreName) {
        return translate(unberscoreName, false);
    }

    /**
     *
     * 将获取数据库类型转化为java类型
     * @param dbTypeName 实际的数据库类型
     * @return
     */
    public static String dbTypeChangeJavaType(String dbTypeName){
        String javaType=null;
        switch(dbTypeName){
            case "VARCHAR" :javaType="String";break;
            case "BIGINT" :javaType="Long";break;
            case "INT" :javaType="Integer";break;
            case "DATETIME" :javaType="Date";break;
            default:javaType="String";break;
        }
        return javaType;
    }

    public static void main(String[] args) {
        String javaname = JavaNameUtil.toPascal("imagines_age_name");
        System.out.println(javaname);
    }
}

然后测试类进行测试:

 

import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


import cn.itrip.beans.util.Attribute;
import cn.itrip.beans.util.FreeMarkerInit;
import cn.itrip.beans.util.JavaNameUtil;
import cn.itrip.beans.util.MetadataUtil;
import freemarker.template.Template;

public class TemplateTest {

    //生成bean
    public void gen1() throws Exception{
        //生成路径
        String savePath="E://springBootLearn//itrip-test//itripbeans//src//main//java//cn//itrip//beans//pojo";
        //获取模板
        Template temp = FreeMarkerInit.getInstance().getDefinedTemplate("javabean.ftl");
        //获取表名集合
        List<String> strs=MetadataUtil.getTableNames();
        for (String str1: strs
             ) {
            //Attribute里面封装模板使用属性
            List<String[]> strList=MetadataUtil.getTableColumnsInfo(str1);
            List<Attribute> attr_list = new ArrayList<Attribute>();
            for (String[] c:strList
                 ) {
                attr_list.add(new Attribute(JavaNameUtil.dbTypeChangeJavaType(c[2]), JavaNameUtil.toCamel(c[0]),c[1]));
            }

            //装换为帕斯卡命名
            String str=JavaNameUtil.toPascal(str1);

            Map<String, Object> root = new HashMap<String, Object>();
            root.put("packageName", "cn.itrip.beans.pojo");
            root.put("className", str);
            root.put("attrs", attr_list);
            OutputStream fos = new  FileOutputStream( new File(savePath, str+".java"));
            Writer out = new OutputStreamWriter(fos);
            temp.process(root, out);
            fos.flush();
            fos.close();
        }

    }



    public static void main(String[] args) {
        TemplateTest test = new TemplateTest();
        try {
            test.gen1();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}