相信大家在開發過程中,應該都用過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);
可以仿照上面的例子,然後再結合官方文件,再比對生成的程式碼,我相信你一定更清楚了。
最後提示一下自己,看文件一定要細心奧。