要“鱼”也要“渔”-java手写代码生成器

遇到重复工作一定想办法将其转化为自动化,我觉得这才是合格的程序猿。我们日常开发中遇到的80%的代码都是增删改查,当然,已经有一些插件,比如MP提供了一整套的代码生成方案,但是提供的基本都是java代码,因为模板语言有太多种,根据每个人每个项目的不同而不同,但是,模板中一样也基本是列表和表单提交,怎么来解决这个重复工作呢?

思路:依然依赖mp组件,因为mp已经提供了对应的BaseService,BaseMapper等,但是本文重点不在于“怎么用”,而在于“怎么写”。“怎么写”属于自己的代码生成器,方便自己随意扩展前端页面生成。

  1. 前提-数据库设计约定:表名、字段名多个单词时下划线分割
  2. 读取数据库,解析各个表字段及备注
  3. 实体类生成:根据字段名解析为对应实体类属性名,同时注释为属性注释
  4. 定义生成模板(xxx.ftl)
  5. 前端页面:数据库字段备注为表单中的字段名,根据字段类型设置输入项的html标签
  6. 分别生成实体类、Mapper、Service、前端页面等
  7. 替换(xxx.ftl)中的变量

一、TableInfo对象

一、JDBC读取数据库表结构

public Map<String, TableInfo> getTables() {
        try {
            DatabaseMetaData databaseMetaData = getConn().getMetaData();

            ResultSet resultSet = databaseMetaData.getColumns(null, "%", "%", "%");

            Map<String, TableInfo> tableInfoMap = new HashMap<>(8);
            while (resultSet.next()) {
                String tableName = resultSet.getString("TABLE_NAME");
                TableInfo tableInfo = tableInfoMap.get(tableName);
                if (tableInfo == null) {
                    tableInfo = new TableInfo(tableName, StringUtil.toHump(tableName,true));
                }

                String colName = resultSet.getString("COLUMN_NAME");
                String remark = resultSet.getString("REMARKS");
                String colType = resultSet.getString("TYPE_NAME");
                List<ColInfo> colInfos = tableInfo.getColInfos();
                if (colInfos == null || colInfos.size() == 0) {
                    colInfos = new ArrayList<>();
                }

                ColInfo colInfo = new ColInfo(colName, colType, remark);
                colInfos.add(colInfo);
                tableInfo.setColInfos(colInfos);

                tableInfoMap.put(tableName, tableInfo);
            }

            return tableInfoMap;
        } catch (SQLException e) {
            e.printStackTrace();
        }

        return null;
    }

二、生成实体类

dataMap 存放渲染ftl模板时的数据。

public void generateModel(String diskPath, TableInfo tableInfo) {
        String templateName = "model.ftl";
        String path = diskPath + File.separator + tableInfo.getModelName() + TableInfo.GEN_SUFFIX;
        Map<String, Object> dataMap = new HashMap<>();
        dataMap.put("ModelName", tableInfo.getModelName());
        dataMap.put("packageName", "com.mos.aaa");
        List<ColInfo> colInfos = tableInfo.getColInfos();
        dataMap.put("fields", colInfos);
        List<String> pcks = new ArrayList<>();
        for (ColInfo info : colInfos){
            if (StringUtil.isNotBlank(info.getFieldType().getPkg()) && !pcks.contains(info.getFieldType().getPkg())){
                pcks.add(info.getFieldType().getPkg());
            }
        }
        dataMap.put("pcks", pcks);
        generateFileByTemplate(templateName, path, dataMap);

    }

三、Model模板定义(用来生成Entity)

package ${packageName};

import com.baomidou.mybatisplus.activerecord.Model;
import java.io.Serializable;
<#if pcks??>
<#list pcks as pck>
import ${pck!};
</#list>
</#if>

public class ${ModelName} extends Model<${ModelName}> {
    private static final long serialVersionUID = 1L;

<#list fields as field>
    /**
    * ${field.remark!}
    */
    private ${field.fieldType.type} ${field.changedColNameLower};
</#list>

<#list fields as field>

    public ${field.fieldType.type} get${field.changedColName}(){
        return ${field.changedColNameLower};
    }

    public void set${field.changedColName}(${field.fieldType.type} ${field.changedColNameLower}){
        this.${field.changedColNameLower} = ${field.changedColNameLower};
    }
</#list>

    @Override
    protected Serializable pkVal() {
        return this.id;
    }

}

四、最终文件生成

private void generateFileByTemplate(final String templateName, String genPath, Map<String, Object> dataMap) {
        try {
            Template template = FreeMarkerTemplateUtils.getTemplate(templateName);
            FileOutputStream fos = new FileOutputStream(new File(genPath));
            Writer out = new BufferedWriter(new OutputStreamWriter(fos, "utf-8"), 10240);
            template.process(dataMap, out);
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }

    }

五、生成结果

六、扩展

以上只是生成简单的Entity实体类示例。一般我们后台页面结构为左、上、下、中四部分,我们只需要生成“中”部分即可,可以根据一个已完成的页面,替换其中的变量即可,生成原理同上。

原文发布于微信公众号 - 陌与尘埃(grq100296)

原文发表时间:2018-10-01

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏MasiMaro 的技术博文

驱动开发中的常用操作

这篇文章会持续更新,由于在驱动中,有许多常用的操作代码几乎不变,而我自己有时候长时间不用经常忘记,所以希望在这把一些常用的操作记录下来,当自己遗忘的时候,有个参...

1764
来自专栏恰童鞋骚年

《T-SQL查询》读书笔记Part 3.索引的基本知识

索引优化是查询优化中最重要的一部分,索引是一种用于排序和搜索的结构,在查找数据时索引可以减少对I/O的需要;当计划中的某些元素需要或是可以利用经过排序的数据时,...

1263
来自专栏专注 Java 基础分享

初识Hibernate之关联映射(二)

     上篇我们介绍了关联映射的几种形式,有单向多对一,单向一对多,还有双向一对多。本篇接着介绍有关关联映射的其他几种映射方式,主要有以下几种: 基于外键的单...

2335
来自专栏架构师之旅

Oracle使用总结之异常篇

1.1 异常处理概念 1.1.1 预定义的异常处理 1.1.2 非预定义的异常处理 1.1.3 用户自定义的异常处理 1.1.4 用户定义的异常处理 1.2 ...

2206
来自专栏王小雷

SAS进阶《深入解析SAS》之Base SAS基础、读取外部数据到SAS数据集

SAS进阶《深入解析SAS》之Base SAS基础、读取外部数据到SAS数据集 前言:在学习完《SAS编程与商业案例》后,虽然能够接手公司的基本工作,但是为了更...

2247
来自专栏积累沉淀

Mybatis多对多关联查询

mybatis3.0添加了association和collection标签专门用于对多个相关实体类数据进行级联查询,但仍不支持多个相关实体类数据的级联保存和级联...

2449
来自专栏有趣的django

Flask构建微电影(二) 第三章、项目分析、搭建目录及模型设计

1770
来自专栏一枝花算不算浪漫

[Java面试三]JavaWeb基础知识总结.

6889
来自专栏陈本布衣

SQLite 带你入门

SQLite数据库相较于我们常用的Mysql,Oracle而言,实在是轻量得不行(最低只占几百K的内存)。平时开发或生产环境中使用各种类型的数据库,可能都需要...

4185
来自专栏数据库

使用VBA创建Access数据表

导读: 本期介绍如何在Access数据库中创建一张空数据表。下期将介绍如何将工作表中的数据存入数据库对应的表中,随后还将介绍如何从数据库的表中取出数据输出到Ex...

2467

扫码关注云+社区

领取腾讯云代金券