MyBatis-Plus Generator自定義模板

Anonymous發表於2021-04-04

相信大家在開發過程中,應該都用過Mybatis-Plus的Generator,但是可能沒有自定義過模板並使用。

每個專案都應該有一個從Controller層到Mapper層的通用模板,來去掉哪些簡單的重複開發工作。

至於如何自定義模板並開發,大家可以先看看這篇博文,以及其附帶的三篇博文,相信您一定有收穫。

Ⅰ、奮鬥青年LOVE

Ⅱ、Github連結

Ⅲ、呵呵彡

如果您看完的話,應該也能手動製作一個自己風格的開發模板。

MyBatis-Plus的預設模板引擎是Velocity,但是這個引擎似乎多年沒有人維護了,所以推薦使用FreeMarker。

官網的例子:

public class CodeGenerator {

    /**
     * <p>
     * 讀取控制檯內容
     * </p>
     */
    public static String scanner(String tip) {
        Scanner scanner = new Scanner(System.in);
        StringBuilder help = new StringBuilder();
        help.append("請輸入" + tip + ":");
        System.out.println(help.toString());
        if (scanner.hasNext()) {
            String ipt = scanner.next();
            if (StringUtils.isNotBlank(ipt)) {
                return ipt;
            }
        }
        throw new MybatisPlusException("請輸入正確的" + tip + "!");
    }

    public static void main(String[] args) {
        // 程式碼生成器
        AutoGenerator mpg = new AutoGenerator();

        // 全域性配置
        GlobalConfig gc = new GlobalConfig();
        String projectPath = System.getProperty("user.dir");
        gc.setOutputDir(projectPath + "/src/main/java"); // 檔案輸出位置,不要把檔案輸出位置寫在那個System.getProperty方法那裡,會輸出null,看清楚哈!!!
        gc.setAuthor("jobob");
        gc.setOpen(false); // 是否開啟檔案位置
        // gc.setSwagger2(true); 實體屬性 Swagger2 註解
        mpg.setGlobalConfig(gc);

        // 資料來源配置
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setUrl("jdbc:mysql://localhost:3306/ant?useUnicode=true&useSSL=false&characterEncoding=utf8");
        // dsc.setSchemaName("public");
        dsc.setDriverName("com.mysql.jdbc.Driver");
        dsc.setUsername("root");
        dsc.setPassword("密碼");
        mpg.setDataSource(dsc);

        // 包配置
        PackageConfig pc = new PackageConfig();
        pc.setModuleName(scanner("模組名"));
        pc.setParent("com.baomidou.ant"); // pc.getParent方法會輸出 包名+模組名
        mpg.setPackageInfo(pc);

        // 自定義配置
        InjectionConfig cfg = new InjectionConfig() {
            @Override
            public void initMap() {
                // to do nothing
            }
        };

        // 如果模板引擎是 freemarker
        String templatePath = "/templates/mapper.xml.ftl";
        // 如果模板引擎是 velocity
        // String templatePath = "/templates/mapper.xml.vm";

        // 自定義輸出配置
        List<FileOutConfig> focList = new ArrayList<>();
        // 自定義配置會被優先輸出
        focList.add(new FileOutConfig(templatePath) {
            @Override
            public String outputFile(TableInfo tableInfo) {
                // 自定義輸出檔名 , 如果你 Entity 設定了前字尾、此處注意 xml 的名稱會跟著發生變化!!
                return projectPath + "/src/main/resources/mapper/" + pc.getModuleName()
                        + "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
            }
        });
        /*
        cfg.setFileCreate(new IFileCreate() {
            @Override
            public boolean isCreate(ConfigBuilder configBuilder, FileType fileType, String filePath) {
                // 判斷自定義資料夾是否需要建立
                checkDir("呼叫預設方法建立的目錄,自定義目錄用");
                if (fileType == FileType.MAPPER) {
                    // 已經生成 mapper 檔案判斷存在,不想重新生成返回 false
                    return !new File(filePath).exists();
                }
                // 允許生成模板檔案
                return true;
            }
        });
        */
        cfg.setFileOutConfigList(focList);
        mpg.setCfg(cfg);

        // 配置模板
        TemplateConfig templateConfig = new TemplateConfig();

        // 配置自定義輸出模板
        //指定自定義模板路徑,注意不要帶上.ftl/.vm, 會根據使用的模板引擎自動識別
        // templateConfig.setEntity("templates/entity2.java");
        // templateConfig.setService();
        // templateConfig.setController();

        templateConfig.setXml(null);
        mpg.setTemplate(templateConfig);

        // 策略配置
        StrategyConfig strategy = new StrategyConfig();
        strategy.setNaming(NamingStrategy.underline_to_camel);
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);
        strategy.setSuperEntityClass("你自己的父類實體,沒有就不用設定!");
        strategy.setEntityLombokModel(true);
        strategy.setRestControllerStyle(true);
        // 公共父類
        strategy.setSuperControllerClass("你自己的父類控制器,沒有就不用設定!");
        // 寫於父類中的公共欄位
        strategy.setSuperEntityColumns("id");
        strategy.setInclude(scanner("表名,多個英文逗號分割").split(","));
        strategy.setControllerMappingHyphenStyle(true);
        strategy.setTablePrefix(pc.getModuleName() + "_");
        mpg.setStrategy(strategy);
        mpg.setTemplateEngine(new FreemarkerTemplateEngine());
        mpg.execute();
    }

}

上面的例子,全域性配置,資料來源配置,包配置,主要看的應該是自定義配置和策略配置。

 // 自定義配置
        InjectionConfig cfg = new InjectionConfig() {
            @Override
            public void initMap() {
                // to do nothing
            }
        };

比如這個,就是給自定義模板提供引數用的,官方示例:

InjectionConfig injectionConfig = new InjectionConfig() {
    //自定義屬性注入:abc
    //在.ftl(或者是.vm)模板中,通過${cfg.abc}獲取屬性
    @Override
    public void initMap() {
        Map<String, Object> map = new HashMap<>();
        map.put("abc", this.getConfig().getGlobalConfig().getAuthor() + "-mp");
        this.setMap(map);
    }
};

我們先看下官方的一個controller模板:

 

 這個是模板的位置,我們就以controller.java.ftl為例:

package ${package.Controller};


import org.springframework.web.bind.annotation.RequestMapping;

<#if restControllerStyle>
import org.springframework.web.bind.annotation.RestController;
<#else>
import org.springframework.stereotype.Controller;
</#if>
<#if superControllerClassPackage??>
import ${superControllerClassPackage};
</#if>

/**
 * <p>
 * ${table.comment!} 前端控制器
 * </p>
 *
 * @author ${author}
 * @since ${date}
 */
<#if restControllerStyle>
@RestController
<#else>
@Controller
</#if>
@RequestMapping("<#if package.ModuleName?? && package.ModuleName != "">/${package.ModuleName}</#if>/<#if controllerMappingHyphenStyle??>${controllerMappingHyphen}<#else>${table.entityPath}</#if>")
<#if kotlin>
class ${table.controllerName}<#if superControllerClass??> : ${superControllerClass}()</#if>
<#else>
<#if superControllerClass??>
public class ${table.controllerName} extends ${superControllerClass} {
<#else>
public class ${table.controllerName} {
</#if>

}
</#if>

這個FreeMarker模板上面是不是有很多引數呢,你肯定會疑惑他們從哪兒來的,官網給其實給出了介紹:

 我們可以Debug官方的CodeGenerator,在AbstractTemplateEngine的getObjectMap方法的末尾打上斷點,

然後再看看objectMap的值有哪些可用。

public Map<String, Object> getObjectMap(TableInfo tableInfo) {
        ConfigBuilder config = this.getConfigBuilder();
        HashMap objectMap;
        if (config.getStrategyConfig().isControllerMappingHyphenStyle()) {
            objectMap = CollectionUtils.newHashMapWithExpectedSize(33);
            objectMap.put("controllerMappingHyphenStyle", config.getStrategyConfig().isControllerMappingHyphenStyle());
            objectMap.put("controllerMappingHyphen", StringUtils.camelToHyphen(tableInfo.getEntityPath()));
        } else {
            objectMap = CollectionUtils.newHashMapWithExpectedSize(31);
        }

        objectMap.put("restControllerStyle", config.getStrategyConfig().isRestControllerStyle());
        objectMap.put("config", config);
        objectMap.put("package", config.getPackageInfo());
        GlobalConfig globalConfig = config.getGlobalConfig();
        objectMap.put("author", globalConfig.getAuthor());
        objectMap.put("idType", globalConfig.getIdType() == null ? null : globalConfig.getIdType().toString());
        objectMap.put("logicDeleteFieldName", config.getStrategyConfig().getLogicDeleteFieldName());
        objectMap.put("versionFieldName", config.getStrategyConfig().getVersionFieldName());
        objectMap.put("activeRecord", globalConfig.isActiveRecord());
        objectMap.put("kotlin", globalConfig.isKotlin());
        objectMap.put("swagger2", globalConfig.isSwagger2());
        objectMap.put("date", (new SimpleDateFormat("yyyy-MM-dd")).format(new Date()));
        objectMap.put("table", tableInfo);
        objectMap.put("enableCache", globalConfig.isEnableCache());
        objectMap.put("baseResultMap", globalConfig.isBaseResultMap());
        objectMap.put("baseColumnList", globalConfig.isBaseColumnList());
        objectMap.put("entity", tableInfo.getEntityName());
        objectMap.put("entitySerialVersionUID", config.getStrategyConfig().isEntitySerialVersionUID());
        objectMap.put("entityColumnConstant", config.getStrategyConfig().isEntityColumnConstant());
        objectMap.put("entityBuilderModel", config.getStrategyConfig().isEntityBuilderModel());
        objectMap.put("chainModel", config.getStrategyConfig().isChainModel());
        objectMap.put("entityLombokModel", config.getStrategyConfig().isEntityLombokModel());
        objectMap.put("entityBooleanColumnRemoveIsPrefix", config.getStrategyConfig().isEntityBooleanColumnRemoveIsPrefix());
        objectMap.put("superEntityClass", this.getSuperClassName(config.getSuperEntityClass()));
        objectMap.put("superMapperClassPackage", config.getSuperMapperClass());
        objectMap.put("superMapperClass", this.getSuperClassName(config.getSuperMapperClass()));
        objectMap.put("superServiceClassPackage", config.getSuperServiceClass());
        objectMap.put("superServiceClass", this.getSuperClassName(config.getSuperServiceClass()));
        objectMap.put("superServiceImplClassPackage", config.getSuperServiceImplClass());
        objectMap.put("superServiceImplClass", this.getSuperClassName(config.getSuperServiceImplClass()));
        objectMap.put("superControllerClassPackage", this.verifyClassPacket(config.getSuperControllerClass()));
        objectMap.put("superControllerClass", this.getSuperClassName(config.getSuperControllerClass()));
        return (Map)(Objects.isNull(config.getInjectionConfig()) ? objectMap : config.getInjectionConfig().prepareObjectMap(objectMap));//這裡上斷點。
    }

那麼加入這些引數不夠用怎麼辦呢,上面提到了可以通過自定義模板引數,不知你們記得嗎,那麼如何引用呢?

我們首先定位到官方的自定義屬性注入

 現在就不會出現了屬性不夠用的情況了,自定義的模板引數通過${cfg.param}來引用。

現在我們來看到官方這段程式碼:

// 如果模板引擎是 freemarker
        String templatePath = "/templates/mapper.xml.ftl";
        // 如果模板引擎是 velocity
        // String templatePath = "/templates/mapper.xml.vm";

        // 自定義輸出配置
        List<FileOutConfig> focList = new ArrayList<>();
        // 自定義配置會被優先輸出
        focList.add(new FileOutConfig(templatePath) {
            @Override
            public String outputFile(TableInfo tableInfo) {
                // 自定義輸出檔名 , 如果你 Entity 設定了前字尾、此處注意 xml 的名稱會跟著發生變化!!
                return projectPath + "/src/main/resources/mapper/" + pc.getModuleName()
                        + "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
            }
        });

templatePath是你自定義的模板的位置,return的是你用模板生成的檔案的位置。

// 配置模板
        TemplateConfig templateConfig = new TemplateConfig();

        // 配置自定義輸出模板
        //指定自定義模板路徑,注意不要帶上.ftl/.vm, 會根據使用的模板引擎自動識別
        // templateConfig.setEntity("templates/entity2.java");
        // templateConfig.setService();
        // templateConfig.setController();

        templateConfig.setXml(null);
        mpg.setTemplate(templateConfig);

這個templateConfig是官方提供的Controller層到Mapper層的基本示例,如果你想自定義的話,就提供自定義模板的位置。

現在我想你已經知道如何自己開發一套模板了吧,我們現在來看看策略配置(上文提到的奮鬥的青年的程式碼)。

// 策略配置
        StrategyConfig strategy = new StrategyConfig();
        // strategy.setCapitalMode(true);// 全域性大寫命名 ORACLE 注意
        //strategy.setTablePrefix(new String[] { "tlog_", "tsys_" });// 此處可以修改為您的表字首
        strategy.setNaming(NamingStrategy.underline_to_camel);// 表名生成策略
        strategy.setInclude(new String[] { "app_certificate" }); // 需要生成的表
        // strategy.setExclude(new String[]{"test"}); // 排除生成的表
        // 自定義實體父類
        // strategy.setSuperEntityClass("com.baomidou.demo.TestEntity");
        // 自定義實體,公共欄位
        // strategy.setSuperEntityColumns(new String[] { "test_id", "age" });
        // 自定義 mapper 父類
        // strategy.setSuperMapperClass("com.baomidou.demo.TestMapper");
        // 自定義 service 父類
        // strategy.setSuperServiceClass("com.baomidou.demo.TestService");
        // 自定義 service 實現類父類
        // strategy.setSuperServiceImplClass("com.baomidou.demo.TestServiceImpl");
        // 自定義 controller 父類
        // strategy.setSuperControllerClass("com.baomidou.demo.TestController");
        // 【實體】是否生成欄位常量(預設 false)
        // public static final String ID = "test_id";
         strategy.setEntityColumnConstant(true);
        // 【實體】是否為構建者模型(預設 false)
        // public User setName(String name) {this.name = name; return this;}
        //strategy.setEntityBuilderModel(true);
        mpg.setStrategy(strategy);

可以仿照上面的例子,然後再結合官方文件,再比對生成的程式碼,我相信你一定更清楚了。

最後提示一下自己,看文件一定要細心奧。

相關文章