代码生成的实现

序言

上一篇,我大概的介绍了一下如何基于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());
        });
    }

用于输出自定义文件,看到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);
})

现在,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());
        });
    }

该方法会接收自定义的模板文件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);
})

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

之前一篇文章,我说的是重写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);
    }

模板中的配置,简单了解一下语法即可,参考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的查询方法查到表数据,可以的话就很方便了。

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

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

为了我的架构师

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值