freemarker代码生成器

代码生成器概述

  • 代码生成器顾名思义就生成代码的一个软件。 为了节省人力成本, 在日常的企业开发中, 代码生成器使用的较为普遍。
     
  • 简单说:可以生成代码的程序
  • 例如:

  • 可以得出如下结论
    • jsp就是一个模板
    • tomcat根据jsp和数据生成了html代码
    • tomcat内部有一个代码生成器可以生成html代码
  • 代码生成器=模板+数据+合成机制
  • 对比如下两个mapper接口

 

 

  • 代码生成器的好处
    • 节省人力成本
    • 易于代码规范控制
  • 代码生成器三要素
    • 模板: 生成文件的模板文件
    • 数据: 生成文件所需要的关键数据
    • 合成机制: 使用数据置换模板中的占位符, 生成新的文件的机制
    • 其中数据我们可以从数据库中进行获取, 模板文件,以及根据模板文件生成代码的机制我们选用较为成熟的模板引擎: FreeMarker,velocity,thymeleaf

freemarker

  • FreeMarker 是一款模板引擎: 即一种基于模板和要改变的数据, 并用来生成输出文本 (HTML 网页、 电子邮件、 配置文件、 源代码等) 的通用工具。 FreeMarker 是免费的, 基于 Apache 许可证 2.0 版本发布。 其模板编写为 FreeMarker Template Language(FTL) , 属于简单、 专用的语言。
     
  • 原理图

  • freemarker数据合成
    • 下载freemarker的jar包

  1.     <dependency>
            <groupId>org.freemarker</groupId>
            <artifactId>freemarker</artifactId>
            <version>2.3.23</version>
        </dependency>
    </dependencies>
  • 创建Configuration对象
    • 作用:环境初始化
    • 常用API

  • 创建Template对象
    • 作用:模板实例化,执行代码生成
    • 常用API
  • 入门案例
    • java代码

  1. public void test1() throws IOException, TemplateException {
        // 初始化freemarker配置
        // 创建configuration对象实例
        Configuration configuration = new Configuration();
        // 获取源文件夹的位置
        String path = this.getClass().getClassLoader().getResource("").getPath();
        // 设置模板文件位置
        configuration.setDirectoryForTemplateLoading(new File(path));
     
        // 通过configuration对象获根据模板名称取到template
        Template template = configuration.getTemplate("/templates/demo.ftl");
     
        // 组装数据
        Map map = new HashMap();
        map.put("name", "jerry");
        // 传参生成数据
        template.process(map, new OutputStreamWriter(new FileOutputStream(new File("d:/a.html"))));
    }
    • 模板代码

  1.     <head>
        </head>
        <body>
           hello ${name}<br>        
        </body>
    </html>

freemarker基本语法

  • 输出基本数据类型或对象属性
    • 输出数据/对象的属性
  •   语法规则:${数据}或者${对象.属性}
    • 输出集合
  1. #list sequence as item> 
       <#ifitem_has_next>
           ...... 
        <#else>
           ...... 
        </#if> 
    </#list>
     
    <#list users as u>
        ${u.username}<br>
    </#list><hr>
    • 判断条件
  2. #if condition1>...
        <#elseif condition2>
           ......
        <#elseif condition3>
           ......
        <#else>
           ......
    </#if>
     
    <#list users as u>
        <#if u.username != "张三">
            ${u.username}<br>
        </#if>
    </#list>
    <hr>
     
     
    <#list users as u>
        <#if u_has_next>
            ${u.username},
        <#else>
            ${u.username}
        </#if>
    </#list>
    <hr>
     
    <#list users as u>
        <#if u.username ="张三">
            ${u.username}<br>
        <#elseif u.username="李四">
            hello${u.username}<br>
        <#else>
            你好${u.username}<br>
        </#if>
    </#list>
    <hr>

获取数据库表数据

 

  • 我们只需要获取到表的数据,就能够通过代码生成器生成mapper
  • 使用JDBC连接数据库
  • <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.8</version>
    </dependency>
  • 获取DatabaseMetaData元数据
    • 获取方式:conn.getMetaData()
  • 获取数据库表信息(主要为表名)
    • ResultSet resultSet = dbmd.getTables(null, null, null, new String[]{"TABLE"})
    • String tableName = resultSet.getString("TABLE_NAME")

/**
 * 获取表信息demo
 */
public class TableDemo {
 
    public static final String DBDRIVER = PropertiesUtils.get("database.properties", "mysql.driver");
    public static final String DBURL = PropertiesUtils.get("database.properties", "mysql.url");
    public static final String DBUSER = PropertiesUtils.get("database.properties", "mysql.user");
    public static final String DBPWD = PropertiesUtils.get("database.properties", "mysql.password");
    private static Connection conn;
    private static DatabaseMetaData metaData;
 
    static {
        try {
            // 加载驱动
            Class.forName(DBDRIVER);
            // 获取数据库连接
            conn = DriverManager.getConnection(DBURL, DBUSER, DBPWD);
            // 得到DatabaseMetaData对象
            metaData = conn.getMetaData();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
 
     // 获取表名
    public static void getTable() {
        try {
            // 拿到表的结果集
            ResultSet resultSet = metaData.getTables(null, null, null, new String[]{"TABLE"});
            // 遍历结果集
            while (resultSet.next()) {
                String tableName = resultSet.getString("TABLE_NAME");
                System.out.println(tableName);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
 
    @Test
    public void test() {
        getTable();
    }
 
}

  • 获取列信息
    • 根据表名获取列信息
      • ResultSet rs = dbmd.getColumns(null, "%", tableName, "%");
    • 获取列名
      • rs.getString("COLUMN_NAME")
    • 获取列注释
      • rs.getString("REMARKS")
    • 获取列类型
      • rs.getString("TYPE_NAME")

// 获取表的列信息
public static void getColumns(String tableName) {
    try {
        // 拿到表的列信息结果集
        ResultSet resultSet = metaData.getColumns(null, "%", tableName, "%");
        // 遍历结果集
        while (resultSet.next()) {
            // 获取列名
            String column_name = resultSet.getString("COLUMN_NAME");
            // 获取列类型
            String type_name = resultSet.getString("TYPE_NAME");
            // 获取列注释
            String remarks = resultSet.getString("REMARKS");
            System.out.println(table_name + "-" + type_name + "-" + remarks);
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}

集成代码生成器

  • 使用Freemarker编写程序源码模板
    • Module.ftl
    • Mapper.ftl
    • Service.ftl
    • ServiceImpl.ftl
  • 分层集成
    • 模板、数据、合成机制
  • 统一配置
    • 配置文件

编写pojo的代码生成器

  • 先将pojo.java改造为pojo.ftl模板

public class 根据表命转化的类名 implements Serializable {
 
    private 表的列类型-java类型 表的列名称;
 
    public void set表的列名首字母大写 (表的列类型-java类型  表的列名称){
        this.表的列名称=表的列名称;
    }
 
    public  Long get表的列名首字母大写(){
        return this.表的列名称;
    }
 
}

  • 封装table和column对象

public class Table {
    // 数据库表名
    private String tableName;
    // 表名转化的类名
    private String className;
    // 表中的列
    private List<Column> list;
}

public class Column {
    // 列名
    private String columnName;
    // 属性名(如果列名和属性名不一致需要转换)
    private String fieldName;
    // 属性名首字母大写
    private String upperFieldName;
    // 列类型
    private String columnType;
    // 列类型转换的java类型
    private String javaType;
    // 列注释
    private String remarks;
}

  • 抽取TableUtil工具类

/**
 * 获取表信息工具类
 */
public class TableUtil {
 
    public static final String DBDRIVER = PropertiesUtils.get("database.properties", "mysql.driver");
    public static final String DBURL = PropertiesUtils.get("database.properties", "mysql.url");
    public static final String DBUSER = PropertiesUtils.get("database.properties", "mysql.user");
    public static final String DBPWD = PropertiesUtils.get("database.properties", "mysql.password");
    private static Connection conn;
    private static DatabaseMetaData metaData;
 
    static {
        try {
            // 加载驱动
            Class.forName(DBDRIVER);
            // 获取数据库连接
            conn = DriverManager.getConnection(DBURL, DBUSER, DBPWD);
            // 得到DatabaseMetaData对象
            metaData = conn.getMetaData();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
 
    // 获取表的列信息
    public static List<Column> getColumns(String tableName) {
        List<Column> columnList = new ArrayList<Column>();
        try {
            // 拿到表的列信息结果集
            ResultSet resultSet = metaData.getColumns(null, "%", tableName, "%");
            // 遍历结果集
            while (resultSet.next()) {
                Column column = new Column();
                // 获取列名
                String column_name = resultSet.getString("COLUMN_NAME");
                // 获取列类型
                String type_name = resultSet.getString("TYPE_NAME");
                // 获取列注释
                String remarks = resultSet.getString("REMARKS");
                //封装columnList
                column.setColumnName(column_name);
                column.setColumnType(type_name);
                column.setFieldName(column_name);
                column.setJavaType(StringUtils.switchType(type_name));
                column.setRemarks(remarks);
                column.setUpperFieldName(StringUtils.captureName(column_name));
                columnList.add(column);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return columnList;
    }
 
    // 获取表名
    public static List<Table> getTable() {
        List<Table> tableList = new ArrayList<Table>();
        try {
            // 拿到表的结果集
            ResultSet resultSet = metaData.getTables(null, null, null, new String[]{"TABLE"});
            // 遍历结果集
            while (resultSet.next()) {
                Table table = new Table();
                String tableName = resultSet.getString("TABLE_NAME");
                table.setClassName(StringUtils.putOffUnderline(tableName));
                table.setTableName(tableName);
                List<Column> columnList = getColumns(tableName);
                table.setList(columnList);
                tableList.add(table);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return tableList;
    }
}

  • 修改模板

public class ${table.className} implements Serializable {
<#list table.list as column>
    // ${column.remarks}
    private ${column.javaType} ${column.fieldName};
</#list>
 
<#list table.list as column>
    public void set${column.upperFieldName} (${column.javaType} ${column.fieldName}){
        this.${column.fieldName}=${column.fieldName};
    }
 
    public ${column.javaType} get${column.upperFieldName}(){
        return this.${column.fieldName};
    }
</#list>
}

  • 改造生成器GeneratorUtils

public class GeneratorUtil {
 
    private static Configuration configuration = new Configuration();
 
    public static void proceed(Map<String, Object> map, String tempName, String savePath) {
        try {
            // 通过configuration对象获根据模板名称取到template
            Template template = configuration.getTemplate(tempName);
            // 传参生成数据
            template.process(map, new OutputStreamWriter(new FileOutputStream(new File(savePath))));
        } catch (IOException e) {
            e.printStackTrace();
        } catch (TemplateException e) {
            e.printStackTrace();
        }
    }
 
    public static void init(String path) {
        try {
            // 设置模板文件位置
            configuration.setDirectoryForTemplateLoading(new File(path));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

  • 编写pojo的生成器并测试

public class PojoHandler {
 
    public void execute() {
        GeneratorUtil.init(this.getClass().getClassLoader().getResource("templates").getPath());
        List<Table> tables = TableUtil.getTable();
        for (Table table : tables) {
            Map<String, Object> map = new HashMap<String, Object>();
            map.put("table", table);
            GeneratorUtil.proceed(map, "pojo.ftl", "d:/data/" + table.getClassName() + ".java");
        }
    }
 
    @Test
    public void test() {
        PojoHandler handler = new PojoHandler();
        handler.execute();
    }
}

猜你喜欢

转载自blog.csdn.net/GoldWashing/article/details/81501819