代码生成器

最近做项目写了一个代码生成器,现在就分享出来给大家,顺便说一下代码生成器是什么东西!

1.什么是代码生成器

代码生成器顾名思义就是一个生成代码的软件。为了节省成本,在日常的企业开发中,代码生成器使用比较普遍。

2.代码生成器的原理

代码生成器的原理就是: 根据模板+ 数据生成不同文件 。比如以下的两段代码。

1.ItripHotelOrderMapper 文件

2.ItripHotelOrderMapper 文件

在上述 ItripHotelOrderMapper 和 ItripHotelOrderMapper 文件中,我们注意到,两个文件的代码很相似。除了高亮部分不一致以外,其它的代码均一致。因此,我们可以设想,我们能不能编写模板生成这样的 Mapper 类。高亮部分使用占位符来进行代替,具体内容根据具体数据进行置换

3. 代码生成器三要素

了解了代码生成器的概念和原理,接下来我开始着手编写属于自己的代码生成器。我将代码生成器的分为三部分:
   ➢ 模板:生成文件的模板文件。
   ➢ 数据:生成文件所需要的关键数据。
   ➢ 合成机制:使用数据置换模板中的占位符,生成新的文件的机制。

 

话不多说,给大家看下代码

表的vo类:

/**
 * 表Vo
 */
public class TableVo {
    private String className;//帕斯卡风格的类名
    private String camelName;//骆驼风格的类名
    private String tableName;//下划线风格的表名
    private List<ColumnVo> columns = new ArrayList<ColumnVo>();//列集合
}

列的vo类:

/**
 * 列Vo类
 */
public class ColumnVo {
    private String dbName;//数据库列名
    private String javaName;//java属性名

    private String dbType;//数据库列类型
    private String javaType;//java属性类型

    private String comm;//注释
    private String upperCaseColumnName;//转成帕斯卡之后的名称
}
 

转换Java格式名称的工具类:

/**
 * 转换java格式名称的工具类
 */
public class JavaNameUtil {
    /**
     * 下划线风格的命名转换为java骆驼命名法或帕斯卡命名法的名称
     * @param underscoreName
     * @param isPascal true:帕斯卡,false:骆驼
     * @return
     */
    public static String translate(String underscoreName, boolean isPascal) {
        StringBuilder result = new StringBuilder();
        if(underscoreName != null && underscoreName.length() > 0) {
            boolean flag = false;
            //首字母特殊处理,转换成帕斯卡命名法或骆驼命名法的名称
            char firstChar = underscoreName.charAt(0);
            if(isPascal) {
                result.append(Character.toUpperCase(firstChar));
            } else {
                result.append(firstChar);
            }
            //第二个之后的所有字符
            for(int i = 1; i < underscoreName.length(); i++) {
                char ch = underscoreName.charAt(i);
                //如果这个字符为下划线,下个字符需要大写处理
                if('_' == ch) {
                    flag = true;
                } else {
                    if(flag) {
                        result.append(Character.toUpperCase(ch));
                        flag = false;
                    } else {
                        result.append(ch);
                    }
                }
            }
        }
        return result.toString();
    }

    /**
     * 将下划线风格的命名直接转换为骆驼命名法的名称
     * @param str
     * @return
     */
    public static String toCamel(String str) {
        return translate(str, false);
    }

    /**
     * 将下划线风格的命名直接转换为帕斯卡命名法的名称
     * @param str
     * @return
     */
    public static String toPascal(String str) {
        return translate(str, true);
    }

    /**
     * 将数据库类型转换成java类型
     * @param dbType
     * @return
     */
    public static String dbTypeToJavaType(String dbType) {
        String javaType = null;
        switch (dbType) {
            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 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/数据库名?useUnicode=true",
                        "账号",
                        "密码");
                meta = conn.getMetaData();//获取数据库的元数据对象
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    //关闭连接
    public static void closeConnection() {
        try {
            if(conn != null || !conn.isClosed()) {
                conn.isClosed();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    //获取所有表名称
    public static String[] getTableNames() {
        openConnection();
        ResultSet rs = null;
        List<String> nameList = new ArrayList<String>();

        try {
            rs = meta.getTables(null, null, null, new String[] {"TABLE"});
            while (rs.next()) {
                String tName = rs.getString("TABLE_NAME");
                nameList.add(tName);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            closeConnection();
        }

        return (String [])nameList.toArray(new String [] {});
    }

    //获取某个表的列信息
    public static List<String []> getTableColumnsInfo(String tableName) throws SQLException {
        openConnection();

        ResultSet rs = meta.getColumns(null, "%", tableName, "%");
        List<String[]> columnInfoList = new ArrayList<String[]>();
        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);
        }

        closeConnection();

        return columnInfoList;
    }

代码生成器类:

**
 * 代码生成器
 */
public class CodeGenerator {
    protected Configuration cfg;//Freemarker配置对象
    protected Map valueMap;//要填充到模板的数据
    protected Template template;//ftl模板对象
    protected String savePath;//保存代码的路径
    protected List<TableVo> tableList;//表信息对象集合
    protected String fileNameSuffix;//生成的文件名后缀

    public CodeGenerator(String ftl) throws Exception {
        cfg = new Configuration();//Freemarker配置对象
        cfg.setClassForTemplateLoading(this.getClass(), "/templates");//设置加载ftl模板的路径
        template = cfg.getTemplate(ftl);//加载模板文件

        valueMap = new HashMap();
        parseMetadata();
    }

    //获取所有的表信息、列信息,并且转换为对象
    public void parseMetadata() throws Exception {
        tableList = new ArrayList<TableVo>();

        //获取所有表名
        String [] tableNameArr = MetadataUtil.getTableNames();
        for (String tName : tableNameArr) {
            TableVo table= new TableVo();//表对象
            //下划线转帕斯卡命名
            table.setClassName(JavaNameUtil.toPascal(tName));
            table.setTableName(tName);
            table.setCamelName(JavaNameUtil.toCamel(tName));
            //调用工具类,获取列信息
            List<String []> colInfoList = MetadataUtil.getTableColumnsInfo(tName);
            for(String[] colInfo : colInfoList) {
                String colName = colInfo[0];//列名
                String comment = colInfo[1];//列注释
                String colType = colInfo[2];//列类型
                ColumnVo column = new ColumnVo();
                column.setDbName(colName);
                column.setDbType(colType);
                column.setComm(comment);
                column.setJavaName(JavaNameUtil.toCamel(colName));
                column.setJavaType(JavaNameUtil.dbTypeToJavaType(colType));
                //列名转帕斯卡命名法,用户生成get和set方法命名
                column.setUpperCaseColumnName(JavaNameUtil.toPascal(colName));
                //将列信息加入表对象的列信息列表中
                table.getColumns().add(column);
            }
            //加入表集合
            tableList.add(table);
        }
        MetadataUtil.closeConnection();//关闭数据库连接
        System.out.println("构建元数据成功!\n\n");
    }

    //抽象生成代码的方法,各子类实现
    public void generateCode() throws Exception {
        System.out.println("--------------开始生成" + template.getName() + "代码生成器开始生成代码");
        OutputStreamWriter writer = null;
        for(TableVo table : tableList) {
            valueMap.put("table", table);
            try {
                //生成的每个代码文件,拼接文件名,创建文件写入器
                writer = new FileWriter(savePath + "/" + table.getClassName() + fileNameSuffix);
                //Freemarker合成数据和模板,输出到代码文件
                this.template.process(valueMap, writer);
                //清空写入器缓冲
                writer.flush();
            } catch (TemplateException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    writer.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        System.out.println("根据" + template.getName() + "模板生成代码成功");
    }

代码生成器入口类main方法:

public class App {
    public static void main(String [] args) throws Exception {
        String myProjectPkg = "cn.itrip";
        //实体类生成器
        CodeGenerator pojoGenerator = new CodeGenerator("pojo.ftl");//设置所用的模板
        pojoGenerator.setSavePath("url");//设置生成代码保存的位置
        pojoGenerator.setFileNameSuffix(".java");//设置文件后缀名
        pojoGenerator.setPackage(myProjectPkg);//项目包名
        //mapper接口生成器
        CodeGenerator mapperGenerator = new CodeGenerator("mapper.ftl");//设置所用的模板
        mapperGenerator.setSavePath("url");//设置生成代码保存的位置
        mapperGenerator.setFileNameSuffix("Mapper.java");//设置文件后缀名
        mapperGenerator.setPackage(myProjectPkg);//项目包名
        //mapper映射文件生成器
        CodeGenerator sqlGenerator = new CodeGenerator("sql.ftl");//设置所用的模板
        sqlGenerator.setSavePath("url");//设置生成代码保存的位置
        sqlGenerator.setFileNameSuffix("Mapper.xml");//设置文件后缀名
        sqlGenerator.setPackage(myProjectPkg);//项目包名
        try {
            //调用代码生成器
            pojoGenerator.generateCode();
            mapperGenerator.generateCode();
            sqlGenerator.generateCode();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

最后在写3个模板文件(pojo、mapper、sql.ftl)然后运行一下App类就可以自动生成代码了

  • 0
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值