MybatisPlus最新代碼生成器(3.5.1+)使用教程(1)——輸出路徑詳細解析


MybatisPlus最新代碼生成器(3.5.1+)使用教程(1)——輸出路徑詳細解析
MybatisPlus最新代碼生成器(3.5.1+)使用教程(2)——輸出文件名詳細解析
MybatisPlus最新代碼生成器(3.5.1+)使用教程(3)——指定數據庫表詳細解析
MybatisPlus最新代碼生成器(3.5.1+)使用教程(4)——文件模板解析

簡介

MyBatisPlus 基於 MyBatis 進行封裝,強調“為簡化開發而生”,極大提高了基於數據庫的 Web 應用開發的效率。鑒於目前網絡上 mybatis-plus-generator 的進階使用教程寥寥無幾,所以我決定拋磚引玉,為大家愉快地使用代碼生成器掃清障礙。

mybatis-plus-generator 替我們生成的文件類型,主要有六種:

代碼生成器(新) 可以看到一個簡單的示例代碼:

FastAutoGenerator.create("url", "username", "password")
    .globalConfig(builder -> {
        builder.author("baomidou") // 設置作者
            .enableSwagger() // 開啟 swagger 模式
            .fileOverride() // 覆蓋已生成文件
            .outputDir("D://"); // 指定輸出目錄
    })
    .packageConfig(builder -> {
        builder.parent("com.baomidou.mybatisplus.samples.generator") // 設置父包名
            .moduleName("system") // 設置父包模塊名
            .pathInfo(Collections.singletonMap(OutputFile.mapperXml, "D://")); // 設置mapperXml生成路徑
    })
    .strategyConfig(builder -> {
        builder.addInclude("t_simple") // 設置需要生成的表名
            .addTablePrefix("t_", "c_"); // 設置過濾表前綴
    })
    .templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默認的是Velocity引擎模板
    .execute();

跟蹤 execute 的代碼,可以追蹤到 com.baomidou.mybatisplus.generator.engine.AbstractTemplateEnginebatchOutput 方法:

public AbstractTemplateEngine batchOutput() {
    try {
        ConfigBuilder config = this.getConfigBuilder();
        List<TableInfo> tableInfoList = config.getTableInfoList();
        tableInfoList.forEach(tableInfo -> {
            Map<String, Object> objectMap = this.getObjectMap(config, tableInfo);
            Optional.ofNullable(config.getInjectionConfig()).ifPresent(t -> {
                t.beforeOutputFile(tableInfo, objectMap);
                // 輸出自定義文件
                outputCustomFile(t.getCustomFile(), tableInfo, objectMap);
            });
            // Mp.java
            outputEntity(tableInfo, objectMap);
            // mapper and xml
            outputMapper(tableInfo, objectMap);
            // service
            outputService(tableInfo, objectMap);
            // MpController.java
            outputController(tableInfo, objectMap);
        });
    } catch (Exception e) {
        throw new RuntimeException("無法創建文件,請檢查配置信息!", e);
    }
    return this;
}

查看所有的 outputXXX 方法代碼,都有 String xxxPath = getPathInfo(OutputFile.xxx);,且文件的輸出路徑統一格式為:{xxxPath}/{文件名}.{文件后綴},例如實體文件的輸出路徑代碼為:

String entityFile = String.format((entityPath + File.separator + "%s" + suffixJavaOrKt()), entityName);

既然,文件完整路徑的拼接規則 我們已經知悉,我們重新聚焦到 getPathInfo 方法源碼:

以下是 ConfigBuilder 的源碼:

也就是說,輸出文件所在文件夾的路徑 是從 com.baomidou.mybatisplus.generator.config.builder.ConfigBuilder 的成員映射 pathInfo 中獲取的。

PackageConfig.packageInfo≠ConfigBuilder.packageInfo

我們在寫 com.baomidou.mybatisplus.generator.FastAutoGenerator 代碼時,可以用 .packageConfig(builder -> builder.pathInfo(customPathInfo)) 設置 PackageConfig.pathInfo

但是,查看 ConfigBuilder 構造函數源碼:

我們自定義的 PackageConfig.pathInfo 需要經過 PathInfoHandler 處理才能得到最終的 ConfigBuilder.pathInfo

PathInfoHandler源碼解析

結論1:自定義路徑優先級高於默認輸出路徑

PathInfoHandler(GlobalConfig globalConfig, TemplateConfig templateConfig, PackageConfig packageConfig) {
  this.outputDir = globalConfig.getOutputDir();
  this.packageConfig = packageConfig;
  // 設置默認輸出路徑
  this.setDefaultPathInfo(globalConfig, templateConfig);
  // 覆蓋自定義路徑
  Map<OutputFile, String> pathInfo = packageConfig.getPathInfo();
  if (CollectionUtils.isNotEmpty(pathInfo)) {
    // 自定義路徑優先級高於默認輸出路徑,如果設置了自定義路徑,將覆蓋默認輸出路徑
    this.pathInfo.putAll(pathInfo);
  }
}

也正因為如此,所以在設置自定義路徑時,需要把 項目目錄模塊名稱src/main/java 或者 src/main/resources,以及包名 全都考慮進去! 例如:

// 自定義路徑
Map<OutputFile, String> customPathInfo = new EnumMap<>(OutputFile.class);
// 項目路徑
String projectPath = System.getProperty("user.dir");
// 子模塊名稱
String webModulePath = "platform-web";
// src/main/java
String srcMain = String.join(File.separator, "src", "main", "java");
// 包名
String packageName = "org.coderead.controller".replace('.', File.separatorChar);
// 自定義控制器路徑
customPathInfo.put(OutputFile.controller, String.join(File.separator, projectPath, webModulePath, srcMain, packageName));
FastAutoGenerator.create("url", "username", "password")
  .packageConfig(builder -> buidler.pathInfo(customPathInfo))
  .execute();

我把 FastAutoGenerator 的客戶端代碼放在了 code-generator 模塊,給大家展示一下 controller 文件的輸出路徑:

結論2:默認所有文件輸出到同一個模塊中

如果跟蹤 PathInfoHandlersetDefaultPathInfo 方法,代碼會來到如下位置:

outputDir 來自於 PathInfoHandler 的構造函數:

而這個全局配置則來自於 FastAutoGenerator 客戶端代碼中的全局配置:

FastAutoGenerator.create("url", "username", "password")
    .globalConfig(builder -> {
        builder.author("baomidou") // 設置作者
            .outputDir("D://"); // 指定輸出目錄
    })
    .execute();

但是,這還不是完整路徑,繼續往下看 PackageConfiggetPackageInfo 的源碼:

關鍵就在於這個私有的無參 getPackageInfo() 方法:

@NotNull
public Map<String, String> getPackageInfo() {
  // 這個方法會被執行多次,因此首次進入該方法需要初始化
  if (packageInfo.isEmpty()) {
    // 指定模塊名稱
    packageInfo.put(ConstVal.MODULE_NAME, this.getModuleName()); // 對應下圖中 .moduleName("system")
    // 獲取 實體類 的包名
    packageInfo.put(ConstVal.ENTITY, this.joinPackage(this.getEntity())); // 對應下圖中 .entity("entity")
    // 獲取 Mapper 接口的包名
    packageInfo.put(ConstVal.MAPPER, this.joinPackage(this.getMapper())); // 對應下圖中 .mapper("dao")
    // 獲取 Mapper.xml 的“包”名
    packageInfo.put(ConstVal.XML, this.joinPackage(this.getXml())); // 對應下圖中 .xml("generate")
    // 獲取 Service 接口的包名
    packageInfo.put(ConstVal.SERVICE, this.joinPackage(this.getService())); // 對應下圖中 .service("service")
    // 獲取 ServiceImpl 實現類的包名
    packageInfo.put(ConstVal.SERVICE_IMPL, this.joinPackage(this.getServiceImpl())); // 這個稍后再說,因為我想放到 service.impl 包中
    // 獲取 Controller 的包名
    packageInfo.put(ConstVal.CONTROLLER, this.joinPackage(this.getController())); // 對應下圖中 .controller("controller")
    packageInfo.put(ConstVal.OTHER, this.joinPackage(this.getOther()));
    packageInfo.put(ConstVal.PARENT, this.getParent()); // 對應下圖中 .parent("org.coderead")
  }
  return Collections.unmodifiableMap(this.packageInfo);
}

以上代碼可以對應 FastAutoGenerator 的客戶端代碼:

結論3:填寫多級包名時用.分隔

繼續閱讀 joinPackage 的源碼,才能搞清楚完整的包路徑

/**
 * 連接父子包名
 */
public String joinPackage(String subPackage) {
  String parent = getParent();
  // 如果 父包名 為空,則父子包名={用戶自定義的 子包名}
  // 如果 父包名 不為空,則父子包名={父包名}.{子包名}
  return StringUtils.isBlank(parent) ? subPackage : (parent + StringPool.DOT + subPackage);
}

/**
 * 父包名
 */
public String getParent() {
  // 如果 moduleName 不為空,則父包名={parent}.{moduleName}
  if (StringUtils.isNotBlank(moduleName)) {
    return parent + StringPool.DOT + moduleName;
  }
  // 如果 moduleName 為空,則父包名={parent}
  return parent;
}

最后,包名中的.會被替換成系統對應的文件分隔符。在 PathInfoHandler.putPathInfo(OutputFile outputFile, String module) 方法中用到的 PathInfoHandler.joinPath 方法如下:

圖中紅框中,就是將父子包名中的.替換成操作系統對應文件分隔符的代碼!

結論4:默認路徑=全局配置輸出路徑 + 父子包名

父子包名={parent}.{moduleName}.{具體某類輸出文件的子包名}

父子包名轉換為完整文件路徑時,會把.替換。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM