入門級的MybatisPlus代碼自動生成器教程

語言: CN / TW / HK

我們使用代碼生成器的目的主要是提供開發效率,減少重複性的工作。在使用Springboot開發過程中,我們使用代碼生成器把大概的模板生成出來,再根據自己的業務特徵進行改動。這樣就可以提高效率

引入依賴

xml <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-generator</artifactId> <version>3.5.2</version> </dependency> <dependency> <groupId>org.apache.velocity</groupId> <artifactId>velocity-engine-core</artifactId> <version>2.3</version> </dependency> <!--糊塗工具包--> <!-- http://mvnrepository.com/artifact/cn.hutool/hutool-core --> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-core</artifactId> <version>5.7.20</version> </dependency> MyBatisPlus提高高度封裝好的代碼生成器模塊,只需要簡單的幾行代碼就能實現。同時也可以根據自己的需求靈活的通過模板話的方式生成代碼。下面我們分別通過這兩種方式來了解一些。

簡單的代碼生成

```java package com.didiplus;

import com.baomidou.mybatisplus.generator.FastAutoGenerator; import com.baomidou.mybatisplus.generator.config.OutputFile; import org.junit.jupiter.api.Test;

import java.util.Collections;

/* * Author: didiplus * Email: [email protected] * CreateTime: 2022/5/6 * Desc:快速生成 / public class FastAutoGeneratorTest {

@Test
public  void fastAutoGeneratorTest(){
    String url="jdbc:mysql://127.0.0.1:3306/didiadmin?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&nullCatalogMeansCurrent=true";
    FastAutoGenerator.create(url,"root","123456")
            .globalConfig(builder -> {
                builder.author("didiplus") // 設置作者
                        .enableSwagger() //開啟 swagger 模式
                        .outputDir("D://autocode");// 指定輸出目錄
            })
            .packageConfig(builder -> {
                builder.parent("com.didiplus.models") // 設置父包名
                        .moduleName("sys") // 設置父包模塊名
                        .pathInfo(Collections.singletonMap(OutputFile.xml,"D://autocode/xml"));
            })
            .strategyConfig(builder -> {
                builder.addInclude("sys_dict_data") // 設置需要生成的表名
                        .addTablePrefix("t_","c_") ; // 設置過濾表前綴
            })
            //  .templateEngine(new FreemarkerTemplateEngine()) 使用Freemarker引擎模板,默認的是Velocity引擎模板
            .execute();
}

}

``` 運行以上代碼,會自動的在D盤生成代碼,但是,生成的代碼只是最基本的模板。 以上生成的代碼都是基於MybatisPlus代碼生成默認模板去生成的。適合絕大多數場景。我們也可以根據自己的模板文件去生成代碼的。

自定義模板生成代碼

實現思路

  • 從數據庫中讀取表的相關信息和表的相關字段
  • 定義相對於的模板文件
  • 組裝模板屬性

定義代碼生成常量

這些常量主要用户後期在組裝模板時,把數據庫類型轉換成Java數據類型需要用到的。 ```java package com.didiplus.constant;

/* * Author: didiplus * Email: 97[email protected] * CreateTime: 2022/5/6 * Desc: 碼 生 成 通 用 常 量 / public class GenerateConstant {

/**
 * 數據庫字符串類型
 */
public static final String[] COLUMN_TYPE_STR =  {"char", "varchar", "nvarchar", "varchar2", "tinytext", "text", "mediumtext", "longtext"};
/**
 * 數據庫時間類型
 */
public static final String[] COLUMN_TYPE_TIME = {"datetime", "time", "date", "timestamp"};
/**
 * 數據庫數字類型
 */
public static final String[] COLUMN_TYPE_NUMBER = {"tinyint", "smallint", "mediumint", "int", "number", "integer", "bit"};
/**
 * 數據庫bigint類型
 */
public static final String[] COLUMN_TYPE_BIGINT = {"bigint"};
/**
 * 數據庫float類型
 */
public static final String[] COLUMN_TYPE_FLOAT = {"float"};
/**
 * 數據庫double類型
 */
public static final String[] COLUMN_TYPE_DOUBLE = {"double"};
/**
 * 數據庫decimal類型
 */
public static final String[] COLUMN_TYPE_DECIMAL = {"decimal"};

/**
 * 字符串類型
 */
public static final String TYPE_STRING = "String";

/**
 * 整型
 */
public static final String TYPE_INTEGER = "Integer";

/**
 * 長整型
 */
public static final String TYPE_LONG = "Long";

/**
 * 浮點型
 */
public static final String TYPE_DOUBLE = "Double";

/**
 * 高精度計算類型
 */
public static final String TYPE_BIGDECIMAL = "BigDecimal";

/**
 * 時間類型
 */
public static final String TYPE_DATE = "Date";

}

```

全局配置

java /** * 全局配置 */ private GlobalConfig.Builder globalConfig() { String projectPath = System.getProperty("user.dir"); return new GlobalConfig.Builder() .fileOverride() // 覆蓋已生成文件 .disableOpenDir() // 禁止打開輸出目錄 默認值:true .author("didiplus") //作者名 .outputDir(projectPath+"/src/main/resources/autocode") // 指定輸出目錄 .enableSwagger(); // 開啟 swagger 模式 默認值:false }

定義生成代碼模板的路徑

java /** * 模板配置 */ private TemplateConfig.Builder templateConfig() { return new TemplateConfig.Builder() .entity("/templates/vm/entity.java") .mapper("/templates/vm/mapper.java") .service("/templates/vm/service.java") .serviceImpl("/templates/vm/serviceimpl.java") .controller("/templates/vm/controller.java") .xml("/templates/vm/mapper.xml"); } 以上的函數是聲明代碼生成根據這些模板去生成對應的模板。

定義各文件生成存儲路徑

java /** * 包配置 */ private PackageConfig.Builder packageConfig() { return new PackageConfig.Builder() .parent(packageName) .moduleName(moduleName) .entity("domain.entity") .mapper("mapper") .service("service") .serviceImpl("service.impl") .xml("mapper.xml") .controller("controller"); }

數據源配置

java /** * 數據源配置 */ private static final DataSourceConfig DATA_SOURCE_CONFIG = new DataSourceConfig .Builder("jdbc:mysql://127.0.0.1:3306/didiadmin?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&nullCatalogMeansCurrent=true","root","123456") .build();

配置策略

java /** * 策略配置 */ private StrategyConfig.Builder strategyConfig() { return new StrategyConfig.Builder() .addInclude(tableName); }

組裝模板屬性

```java /* * 注入配置 / private InjectionConfig.Builder injectionConfig(){ Map map = new HashMap<>(); setAttr(tableName, DATA_SOURCE_CONFIG, map); return new InjectionConfig.Builder().customMap(map);

}


/**
 * 組裝模板屬性
 *
 * @param tableName        表名
 * @param dataSourceConfig 數據源
 * @param map              模板裏面 自定義的屬性
 * @param  表前綴
 */
private void setAttr(String tableName, DataSourceConfig dataSourceConfig, Map<String, Object> map ){
    List<Map<String, Object>> columns = new ArrayList<>();
    // 獲取表信息sql
    String tableSql = "select table_name , table_comment from information_schema.tables " +
            "where table_schema = (select database()) and table_name = '" + tableName + "'";
    // 獲取字段信息sql
    String columnSql = "select column_name , data_type , column_comment from information_schema.columns " +
            "where table_name = '" + tableName + "' and table_schema = (select database()) and column_name != 'id_new'";

    // 利用現有的dataSourceConfig來獲取數據庫連接,查詢表字段及備註等信息
    try(
            Connection conn = dataSourceConfig.getConn();
            PreparedStatement psTable = conn.prepareStatement(tableSql);
            ResultSet rsTable = psTable.executeQuery();
            PreparedStatement pscolumns= conn.prepareStatement(columnSql);
            ResultSet rscolumns = pscolumns.executeQuery();
    ){
        if(rsTable.next()){
            String table_name = rsTable.getString("table_name");
            map.put("tableName",table_name);
            map.put("comment",rsTable.getString("table_comment"));
            // 類名 大駝峯
            map.put("upperClassName", StrUtil.upperFirst(StrUtil.toCamelCase(table_name)));
            // 對象名 小駝峯
            map.put("lowerClassName",StrUtil.toCamelCase(table_name));
        }
        while (rscolumns.next()){
            Map<String, Object> columnMap = new HashMap<>();
            // 列名字、數據類型、java屬性名字、java屬性類型、備註
            columnMap.put("column_name",rscolumns.getString("column_name"));
            columnMap.put("data_type",rscolumns.getString("data_type"));
            columnMap.put("javaLowerAttrName",StrUtil.toCamelCase(rscolumns.getString("column_name")));
            columnMap.put("javaAttrType",columnTypeToJavaType(rscolumns.getString("data_type")));
            columnMap.put("column_comment", rscolumns.getString("column_comment"));
            columns.add(columnMap);
        }
    } catch (Exception e) {
        log.info("----------sql執行出錯");
        e.printStackTrace();
    }
    map.put("columns",columns);
    map.put("datetime", DateUtil.now());
    map.put("packageName", packageName);
    map.put("moduleName", moduleName);
}

/**
 * 數據庫類型轉換為java類型
 *
 * @param columnType 數據庫類型
 * @return java類型
 */
private String  columnTypeToJavaType(String columnType) {
    if(StrUtil.isNotEmpty(columnType)){
        if(Arrays.asList(GenerateConstant.COLUMN_TYPE_STR).contains(columnType)){
            return GenerateConstant.TYPE_STRING;
        }
        if(Arrays.asList(GenerateConstant.COLUMN_TYPE_TIME).contains(columnType)){
            return GenerateConstant.TYPE_DATE;
        }
        if (Arrays.asList(GenerateConstant.COLUMN_TYPE_NUMBER).contains(columnType)) {
            return GenerateConstant.TYPE_INTEGER;
        }
        if (Arrays.asList(GenerateConstant.COLUMN_TYPE_BIGINT).contains(columnType)) {
            return GenerateConstant.TYPE_LONG;
        }
        if (Arrays.asList(GenerateConstant.COLUMN_TYPE_FLOAT).contains(columnType)) {
            return GenerateConstant.TYPE_DOUBLE;
        }
        if (Arrays.asList(GenerateConstant.COLUMN_TYPE_DOUBLE).contains(columnType)) {
            return GenerateConstant.TYPE_DOUBLE;
        }
        if (Arrays.asList(GenerateConstant.COLUMN_TYPE_DECIMAL).contains(columnType)) {
            return GenerateConstant.TYPE_BIGDECIMAL;
        }
    }
    return  null;
}

```

定義對應的模板文件

在項目的資源文件夾templats中創建vm文件夾存放模板文件

entity.java.vm

```java package ${packageName}.${moduleName}.domain.entity; #set($list=["createBy","createTime","createName", "updateBy", "updateName","updateTime", "deleteFlag"]) import com.didiplus.common.base.BaseDomain; import lombok.Data; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty;

import java.util.Date;

import com.baomidou.mybatisplus.annotation.TableName;

/* * @author ${author} * @date ${datetime} * * @description ${comment}(${upperClassName}實體類) / @Data @TableName("${tableName}") @ApiModel(value = "${comment}", description = "${comment}對象 ${lowerClassName}") public class ${upperClassName}Entity extends BaseDomain {

#foreach ($column in $columns)
    ##    排除父類字段
    #if($list.contains($column.javaLowerAttrName))
    #else
        /**
         * $column.column_comment
         */
        @ApiModelProperty(value = "$column.column_comment")
        private $column.javaAttrType $column.javaLowerAttrName;
    #end
#end

} ```

mapper.java.vm

```java package ${packageName}.${moduleName}.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper; import ${packageName}.${moduleName}.domain.entity.${upperClassName}Entity; import org.apache.ibatis.annotations.Mapper;

/* * @author ${author} * @date ${datetime} * * @description ${comment} / @Mapper public interface ${upperClassName}Mapper extends BaseMapper<${upperClassName}Entity> {

} ```

mapper.xml.vm

```java

<resultMap id="${lowerClassName}Map" type="${packageName}.${moduleName}.domain.${upperClassName}">
    #foreach($column in $columns)
        <result column="${column.column_name}" property="${column.javaLowerAttrName}"/>
    #end
</resultMap>

```

service.java.vm

```java package ${packageName}.${moduleName}.service; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.service.IService; import ${packageName}.${moduleName}.domain.entity.${upperClassName}; import com.didiplus.common.web.domain.PageDomain;

/* * @author ${author} * @date ${datetime} * * @description ${comment} / public interface I${upperClassName}Service extends IService<${upperClassName}Entity> {

/**
* 分頁查詢
* @param pageDomain
* @return
*/
IPage<${upperClassName}Entity> page(PageDomain pageDomain);

} ```

serviceImpl.java.vm

```java package ${packageName}.${moduleName}.service.impl;

import com.didiplus.common.web.domain.PageDomain; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import ${packageName}.${moduleName}.domain.entity.${upperClassName}Entity; import ${packageName}.${moduleName}.mapper.${upperClassName}Mapper; import ${packageName}.${moduleName}.service.I${upperClassName}Service; import org.springframework.stereotype.Service; import javax.annotation.Resource; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; /* * @author ${author} * @date ${datetime} * * @description ${comment} / @Service public class ${upperClassName}ServiceImpl extends ServiceImpl<${upperClassName}Mapper, ${upperClassName}Entity> implements I${upperClassName}Service {

    @Resource
    ${upperClassName}Mapper ${lowerClassName}Mapper;
    @Override
    public IPage<${upperClassName}Entity> page(PageDomain pageDomain) {
        IPage<${upperClassName}Entity> page = new Page<>(pageDomain.getPage(),pageDomain.getLimit());
        return ${lowerClassName}Mapper.selectPage(page,null);
    }

}

```

controller.java.vm

```java package ${packageName}.${moduleName}.controller;

import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import ${packageName}.${moduleName}.domain.entity.${upperClassName}Entity; import ${packageName}.${moduleName}.service.I${upperClassName}Service; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.extern.slf4j.Slf4j; import com.didiplus.common.web.domain.PageDomain; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import com.didiplus.common.base.ValidGroup; import javax.annotation.Resource;

/* * @author ${author} * @date ${datetime} * * @description ${comment} / @Slf4j @RestController @RequestMapping("/${lowerClassName}") @Api(value = "${lowerClassName}", tags = "${comment}管理模塊") public class ${upperClassName}Controller {

@Resource
private I${upperClassName}Service ${lowerClassName}Service;

/**
 * 分頁查詢
 * @param pageDomain 分頁對象
 * @param ${lowerClassName}Entity ${comment}
 * @return IPage
 */
@ApiOperation(value = "分頁查詢", notes = "分頁查詢")
@GetMapping("/page")
public IPage get${upperClassName}Page(@RequestBody PageDomain pageDomain) {
    return ${lowerClassName}Service.page(pageDomain);
}

/**
 * 新增${comment}
 * @param ${lowerClassName}Entity ${comment}
 * @return Result
 */
@ApiOperation(value = "新增${comment}", notes = "新增${comment}")
@PostMapping
public String save(@Validated(ValidGroup.Crud.Create.class)  @RequestBody ${upperClassName}Entity ${lowerClassName}Entity) {
    return  ${lowerClassName}Service.save(${lowerClassName}Entity)? "添加成功":"添加失敗";

}

/**
 * 修改${comment}
 * @param ${lowerClassName}Entity ${comment}
 * @return Result
 */
@ApiOperation(value = "修改${comment}", notes = "修改${comment}")
@PutMapping
public String updateById(@Validated(ValidGroup.Crud.Update.class)  @RequestBody ${upperClassName}Entity ${lowerClassName}Entity) {
    return  ${lowerClassName}Service.updateById(${lowerClassName}Entity)? "修改成功":"修改失敗";

}

/**
 * 通過id刪除${comment}
 * @param id id
 * @return Result
 */
@ApiOperation(value = "通過id刪除${comment}", notes = "通過id刪除${comment}")
@DeleteMapping("/{id}")
public String removeById(@PathVariable Integer id) {
    return ${lowerClassName}Service.removeById(id)? "刪除成功":"刪除失敗";
}

}

```

定義啟動類

```java @Test public void generatorCode(){

    AutoGenerator generator = new AutoGenerator(DATA_SOURCE_CONFIG);
    generator.strategy(strategyConfig().build());
    generator.injection(injectionConfig().build());
    generator.global(globalConfig().build());
    generator.template(templateConfig().build());
    generator.packageInfo(packageConfig().build());
    generator.execute();


}

``` 執行以上函數就可以自動生成代碼了,如下圖: 目前代碼自動生成器只是一個腳本方式運行,後續我們會把它集成到頁面上,通過圖形界面的方式去操作。好了今天就分享到這裏了,有什麼問題歡迎留意。更多關注公眾號“攻城獅成長日記”。

didi.png