• 代码生成的实现


    序言

    上一篇,我大概的介绍了一下如何基于Mybatis-Plus做自己的代码生成模板。快速上手

    在这期间呢,今天是五月二十一号,我也把代码生成完善了一下,实现了添加修改删除批量删除分页查找id查找这6个功能的代码自动生成。其中也包括VO类、Form类这些用于返回、接收数据的类的生成。此外,还支持单表父子关系的代码生成(例如菜单表通过parent_id来区分父子关系)

    那么下面就一起看看我是如何实现的吧。

    实现

    其实,思路就是我上一篇介绍的,把自己需要的数据填进去就行了,然后在模板中使用。

    但是,我在做VO、Form类的生成的时候遇到了一些问题。

    因为Mybatis-Plus的默认包路径只有:controller、service、serviceImpl、entity、mapper、xml和一个other的包路径,在配置时也只能指定这几个路径。

    最开始,我的想法是,用other这个包路径指定为VO和Form的输出路径,但是这样写的话,VO和Form都在一个包下面了,这肯定不行。最终效果应该是这样的:image-20220521210214460

    它们应该在不同的包下面,

    解决思路有两种:

    1. form、vo和entity是在同一个包的,可以不用other路径,直接用entity配置的路径domain。只是下面的form、vo包需要我们稍作处理。
    2. 使用other,将其指定为和entity一样的路径—domain,form和vo包同样需要做处理

    两种方式本质上没区别,我用的方式二。

    那么我们如何对form、vo包处理呢?这里需要介绍plus的outputCustomFile方法。

    outputCustomFile方法

    该方法位于AbstractTemplateEngine抽象类下,所有的模板引擎都实现了这个类:

    image-20220521211021685

    该方法源码:

        /**
         * 输出自定义模板文件
         *
         * @param customFile 自定义配置模板文件信息
         * @param tableInfo  表信息
         * @param objectMap  渲染数据
         * @since 3.5.1
         */
        protected void outputCustomFile(@NotNull Map<String, String> customFile, @NotNull TableInfo tableInfo, @NotNull Map<String, Object> objectMap) {
            String entityName = tableInfo.getEntityName();
            String otherPath = getPathInfo(OutputFile.other);
            customFile.forEach((key, value) -> {
                String fileName = String.format((otherPath + File.separator + entityName + File.separator + "%s"), key);
                outputFile(new File(fileName), objectMap, value, getConfigBuilder().getInjectionConfig().isFileOverride());
            });
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    用于输出自定义文件,看到String fileName =这句代码了吗?它就是在设置文件的输出位置。

    所以我们的vo、form单独分包,就需要在这里做手脚了。

    之前,我们已经写了一个类MyVelocityTemplateEngine继承了VelocityTemplateEngine,也就是间接继承了AbstractTemplateEngine抽象类,所以在MyVelocityTemplateEngine中重写这个方法就好了。

    万事具备,只欠东风。不知道你有没有发现该方法的注释写的是:输出自定义模板文件,

    对的,这个方法是处理自定义模板并指定生成文件的输出路径。那么什么是自定义模板?

    plus默认只会找:controller、service、serviceImpl、entity、mapper、xml这几个模板文件,其它的就叫自定义模板了。

    所以,要想让这个方法执行,我们首先要想办法告诉plus我们的自定义模板在哪。好在,plus为我们提供了实现:

    image-20220521212145738

    我们需要使用注入配置,以前我一直不知道注入配置是干啥的,现在明白了。

    在代码生成配置中,加上这样的代码:

    // 首先自定义vo、form的模板路径
    Map<String, String> customFileMap = new HashMap();
    customFileMap.put("Form", "/templates/form.java.vm");
    customFileMap.put("VO", "/templates/vo.java.vm");
    // 注入
    .injectionConfig(builder -> {
        builder..customFile(customFileMap);
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    现在,outputCustomFile方法就会执行了,现在重写这个方法吧:

    @Override
        protected void outputCustomFile( Map<String, String> customFile,  TableInfo tableInfo,  Map<String, Object> objectMap) {
            String entityName = tableInfo.getEntityName();
            String otherPath = getPathInfo(OutputFile.other);
            customFile.forEach((key, value) -> {
                String fileName = "";
                if ("Form".equals(key)) {
                    fileName = String.format((otherPath + File.separator + "form" + File.separator + entityName  + "%s" + suffixJavaOrKt()), key);
                }
                else {
                    fileName = String.format((otherPath + File.separator + "vo" + File.separator + entityName  + "%s" + suffixJavaOrKt()), key);
                }
                outputFile(new File(fileName), objectMap, value, getConfigBuilder().getInjectionConfig().isFileOverride());
            });
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    该方法会接收自定义的模板文件map,所以在定义fileName的时候判断一下map的key,为form就在路径中加上form,vo的话就加vo。

    这样我们的自定义文件就能生成到指定位置了。

    关于自定义数据

    image-20220521212829138

    注入配置中,也可以用一个map去添加自定义数据,比如我的:

    // 自定义数据
    Map<String, Object> customDataMap = new HashMap();
    	// 数据校验分组
    customDataMap.put("addGroup", "com.monkeylessey.group.AddGroup");
    customDataMap.put("updateGroup", "com.monkeylessey.group.UpdateGroup");
    	// 统一响应类
    customDataMap.put("responseDataName", "ResponseData");
    customDataMap.put("responseDataPath", "com.monkeylessey.response.ResponseData");
    
    // 注入
    .injectionConfig(builder -> {
        builder.customMap(customDataMap)
            .customFile(customFileMap);
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    我们在模板中就能直接使用自定义的数据了。

    之前一篇文章,我说的是重写write方法然后加入自定义数据,当然两种方式都可以的。

    自定义Map这种方式适用于死数据,不需要处理。

    而重写write方法这种方式适用于定义需要处理plus自带数据才能得到的数据。比如依赖注入的对象名首字母小写,这种在配置map时是无法处理的,因为plus的默认数据都还没有。这样的例子还有路径、表字段信息等等。

    因为自定义数据,一般比较多。所以建议大家写不同的类去处理不同的自定义数据,这样代码不冗长也便于维护。(在write方法中)比如我定义了FormHandler去处理form的数据,VoHandler去处理vo的数据。

    @Override
        public void writer(Map<String, Object> objectMap, String templatePath, File outputFile) throws Exception {
            // 获取table
            TableInfo tableInfo = (TableInfo) objectMap.get("table");
            // 取出Service接口的名字,进行首字母小写处理
            String serviceNameFirstWord = tableInfo.getServiceName().substring(0,1).toLowerCase();
            String serviceNameFirstWordToLower = serviceNameFirstWord + tableInfo.getServiceName().substring(1);
            objectMap.put("serviceNameFirstWordToLower", serviceNameFirstWordToLower);
    
            // 取出mapper接口的名字,进行首字母小写处理
            String mapperNameFirstWord = tableInfo.getMapperName().substring(0, 1).toLowerCase();
            String mapperNameFirstWordToLower = mapperNameFirstWord + tableInfo.getMapperName().substring(1);
            objectMap.put("mapperNameFirstWordToLower", mapperNameFirstWordToLower);
    
            // 对Form、VO文件的处理
            new FormHandler().handler(objectMap);
            super.writer(objectMap, templatePath, outputFile);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    模板中的配置,简单了解一下语法即可,参考plus自带的,很容易上手。这里就不叙述了。

    效果展示

    先看下生成的目录结构:

    image-20220521214806854

    controller:

    image-20220521214908374

    service:

    image-20220521215217785

    entity:

    image-20220521215355205

    form:

    image-20220521215530028

    图太多占篇幅,就放几张代表的吧。

    总结

    目前生成的代码没有错误,只不过测试得比较少。因为现在的局限性(只能单表生成代码),所以问题应该是没什么大问题了,后期表多了磨合磨合就差不多了。

    现在代码生成只是后端使用main方法执行,后面需要做前端(快了),要把很多参数提取出来供前端设置。

    目前只能单表确实局限性很大,我也研究了基于plus的机制能不能实现一对多、一对一这种关系,答案是能,大致思路:如果你一次生成多个表,plus是循环生成代码的,所以表A是拿不到表B的信息的。如果A包含B,那么先生成B的信息。将b的数据保存到中间层如redis或者ThreadLocal中并且需要判定哪次是存哪次是取,要保证生成A的时候能拿到B的数据)我目前还不确定能不能自己调用plus的查询方法查到表数据,可以的话就很方便了。

    总之,代码生成将告一段落了。虽然有局限性,但是我估算了一下一个表能省半小时吧,摸鱼它不香吗哈哈。而且一对一、一对多修改起来也不是很麻烦了。
    如果你有什么疑问可以私信我。

  • 相关阅读:
    NO.2 | 977.有序数组的平方 ,209.长度最小的子数组 ,59.螺旋矩阵II
    还在用微信截图吗?这2个免费软件你不能错过
    十天学前端(二)
    2022年9月电子学会Python等级考试试卷(三级)答案解析
    【论文解读系列】NER方向:LatticeLSTM (ACL2018)
    docker配置项目镜像
    C#进阶07——反射和特性
    雷达信号处理框架,中远距离和近距离处理的框架(启发---推荐多看)
    呼声与现实:WPS Office 64位版何时到来?
    adb-环境安装
  • 原文地址:https://blog.csdn.net/qq_42682745/article/details/124906331