一文带你掌握MyBatis-Plus代码生成器:从入门到精通,实现原理与自定义模板全解析

322 阅读9分钟

1.背景:为什么需要代码生成器?

近年来,随着AI编程和低代码平台的兴起,开发效率成为技术圈的热门话题。虽然这些工具能够在一定程度上提升开发效率,但对于复杂的业务逻辑和定制化需求,仍然需要程序员手动编写代码。

特别是在后端开发中,业务接口的编写往往存在大量重复性工作:实体类、Mapper、Service、Controller等基础代码结构相似,却需要反复编写。这时候,一个优秀的代码生成器就能大显身手。

今天我要详细介绍的就是MyBatis-Plus Generator(简称MPG),这款强大的代码生成工具能够根据数据库表结构,自动生成包括Entity、Mapper、Service、Controller在内的全套代码,真正实现"一键生成,开箱即用"。

2.快速入门:5分钟上手代码生成

2.1 引入依赖

  <!-- 代码生成器  -->
  <dependency>
      <groupId>com.baomidou</groupId>
      <artifactId>mybatis-plus-generator</artifactId>
      <version>3.5.12</version>
  </dependency>
  <!-- 模板引擎(Freemarker示例) -->
  <dependency>
      <groupId>org.freemarker</groupId>
      <artifactId>freemarker</artifactId>
      <version>2.3.32</version>
  </dependency>

2.2 准备测试数据表

创建一张测试用户表tb_user:

CREATE TABLE `tb_user` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `user_no` varchar(255) NOT NULL COMMENT '编号',
  `name` varchar(255) DEFAULT NULL COMMENT '昵称',
  `email` varchar(255) DEFAULT NULL COMMENT '邮箱',
  `phone` varchar(255) NOT NULL COMMENT '手机号',
  `gender` tinyint(4) NOT NULL DEFAULT '0' COMMENT '性别  0:男生   1:女生',
  `birthday` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '出生日期',
  `is_delete` tinyint(4) NOT NULL DEFAULT '0' COMMENT '删除标志 0:否  1:是',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  `update_time` datetime DEFAULT NULL COMMENT '更新时间',
  `creator` bigint(20) DEFAULT NULL COMMENT '创建人',
  `updater` bigint(20) DEFAULT NULL COMMENT '更新人',
  `address` varchar(1024) DEFAULT NULL COMMENT '地址',
  `role_id` varchar(100) DEFAULT NULL COMMENT '角色id',
  `hobby` varchar(255) DEFAULT NULL COMMENT '爱好',
  `remark` varchar(255) DEFAULT NULL COMMENT '个人说明',
  `org_id` bigint(20) DEFAULT NULL COMMENT '公司id',
  PRIMARY KEY (`id`) USING BTREE,
  UNIQUE KEY `uk_user_no` (`user_no`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;

2.3 基础配置生成代码

MPG提供了流畅的Builder模式API,让配置过程直观易懂:

public class CodeGeneratorTest {
​
    public static void main(String[] args) {
        // 使用 FastAutoGenerator 快速配置代码生成器
        FastAutoGenerator.create("jdbc:mysql://127.0.0.1:3306/db_test?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true",
                        "root",
                        "root")
                .globalConfig(builder -> {  // 全局配置
                    builder.author("ZFJ") // 设置作者
                            .commentDate("yyyy-MM-dd")  // 设置日期
                            .enableSpringdoc()  // 开启openapi3文档注释
                            .outputDir("src/main/java")  // 输出目录
                            .disableOpenDir(); // 不打开路径
                })
                .packageConfig(builder -> {
                    builder.parent("com.shepherd.example") // 设置父包名
                            .entity("entity") // 设置entity实体类包名
                            .mapper("dao") // 设置Mapper接口包名
                            .service("service") // 设置Service接口包名
                            .serviceImpl("service.impl") // 设置Service实现类包名
                            .xml("mappers"); // 设置 MapperXML文件包名
                })
                .strategyConfig(builder -> {
                    builder.addInclude("tb_user") // 设置需要生成的表名
                            .addTablePrefix("tb_") // 添加表前缀
                            // 设置实体类
                            .entityBuilder()
                            .enableLombok(new ClassAnnotationAttributes("@Data","lombok.Data")) // 启用 Lombok
                            .enableTableFieldAnnotation() // 启用字段注解
                            .javaTemplate("/templates/entity.java") // 设置实体类模板
                            // 设置mapper接口
                            .mapperBuilder()
                            .mapperTemplate("/templates/mapper.java") // 设置mapper目标
                            .convertMapperFileName((entityName -> entityName + "DAO")) // 设置mapper接口文件名
                            .enableBaseResultMap()
                            .enableBaseColumnList()
                            // 设置service接口
                            .serviceBuilder()
                            .serviceTemplate("/templates/service.java") // 设置Service模板
                            .convertServiceFileName((entityName -> entityName + "Service")) // 设置service文件名
                            .serviceImplTemplate("/templates/serviceImpl.java") // 设置ServiceImpl模板
                            // 设置controller类
                            .controllerBuilder()
                            .template("/templates/controller.java")
                            .enableRestStyle(); // 启用 REST 风格
                })
                .templateEngine(new FreemarkerTemplateEngine()) // 使用 Freemarker 模板引擎
                .execute(); // 执行生成
    }
}
​

运行生成代码文件如下所示:

3.核心配置

3.1 全局配置(GlobalConfig)

全局配置提供了对代码生成器整体行为的设置,包括输出目录、作者信息、Kotlin 模式、Swagger 集成、Springdoc、时间类型策略等

方法说明示例
disableOpenDir()禁止自动打开输出目录默认值: true
outputDir(String)指定代码生成的输出目录如:System.getProperty("user.dir") + "/src/main/java"
author(String)设置作者名baomidou 默认值: 配置文件中的作者名
enableKotlin()开启 Kotlin 模式默认值: false
enableSwagger()开启 Swagger 模式默认值: false
dateType(DateType)设置时间类型策略DateType.ONLY_DATE 默认值: DateType.TIME_PACK
commentDate(String)设置注释日期格式默认值: yyyy-MM-dd
enableSpringdoc开启Springdoc文档默认值:false

3.2 策略配置(StrategyConfig)

策略配置是MPG的核心部分,它允许开发者根据项目需求定制代码生成的规则,包括命名模式、表和字段的过滤、以及各个代码模块的生成策略。

方法说明示例
enableCapitalMode开启大写命名默认值: false
enableSkipView开启跳过视图默认值: false
disableSqlFilter禁用 SQL 过滤默认值: true,如果 SQL 过滤不支持,可以关闭此选项
enableSchema启用 schema默认值: false,多 schema 场景时启用
likeTable(LikeTable)模糊表匹配(SQL 过滤)notLikeTable 互斥,只能配置一项
notLikeTable(LikeTable)模糊表排除(SQL 过滤)likeTable 互斥,只能配置一项
addInclude(String…)增加表匹配(内存过滤)addExclude 互斥,只能配置一项,支持正则匹配,如 ^t_.* 匹配所有以 t_ 开头的表名
addExclude(String…)增加表排除匹配(内存过滤)addInclude 互斥,只能配置一项,支持正则匹配,如 .*st$ 匹配所有以 st 结尾的表名
addTablePrefix(String…)增加过滤表前缀
addTableSuffix(String…)增加过滤表后缀
addFieldPrefix(String…)增加过滤字段前缀
addFieldSuffix(String…)增加过滤字段后缀
outputFile内置模板输出文件处理参考测试用例 H2CodeGeneratorTest.testOutputFile
entityBuilder实体策略配置
controllerBuilderController 策略配置
mapperBuilderMapper 策略配置
serviceBuilderService 策略配置

可以看到策略配置对提供统一的生成规则配置,还支持entity, mapper, service, controller等类分别配置,条理清晰,功能强大。碍于篇幅问题,我这里就只展示一下实体类entity的生成策略配置,其他的详见官网文档。

Entity 策略配置

实体策略配置用于定制实体类的生成规则,包括父类、序列化版本 UID、文件覆盖、字段常量、链式模型、Lombok 模型等

方法说明示例
nameConvert(INameConvert)名称转换实现
superClass(Class<?>)设置父类BaseEntity.class
superClass(String)设置父类com.baomidou.global.BaseEntity
disableSerialVersionUID禁用生成 serialVersionUID默认值: true
enableFileOverride覆盖已生成文件默认值: false
enableColumnConstant开启生成字段常量默认值: false
enableChainModel开启链式模型默认值: false
enableLombok开启 Lombok 模型默认值: false 默认只有Getter,Setter,自3.5.10后增加ToString
enableRemoveIsPrefix开启 Boolean 类型字段移除 is 前缀默认值: false
enableTableFieldAnnotation开启生成实体时生成字段注解默认值: false
enableActiveRecord开启 ActiveRecord 模型默认值: false
versionColumnName(String)乐观锁字段名(数据库字段)versionColumnNameversionPropertyName 二选一即可
versionPropertyName(String)乐观锁属性名(实体)versionColumnNameversionPropertyName 二选一即可
logicDeleteColumnName(String)逻辑删除字段名(数据库字段)logicDeleteColumnNamelogicDeletePropertyName 二选一即可
logicDeletePropertyName(String)逻辑删除属性名(实体)logicDeleteColumnNamelogicDeletePropertyName 二选一即可
naming数据库表映射到实体的命名策略默认下划线转驼峰命名: NamingStrategy.underline_to_camel
columnNaming数据库表字段映射到实体的命名策略默认为 null,未指定按照 naming 执行
addSuperEntityColumns(String…)添加父类公共字段
addIgnoreColumns(String…)添加忽略字段
addTableFills(IFill…)添加表字段填充
addTableFills(List)添加表字段填充
idType(IdType)全局主键类型
convertFileName(ConverterFileName)转换文件名称
formatFileName(String)格式化文件名称
toString(boolean)是否生成ToString方法默认为true, 自3.5.10开始
fieldUseJavaDoc启用字段文档注释默认为true, 自3.5.10开始
classAnnotations(ClassAnnotationAttributes)添加实体类注解自3.5.10开始
tableAnnotationHandler表注解处理器自3.5.10开始
tableFieldAnnotationHandler字段注解处理器自3.5.10开始
enableLombok(ClassAnnotationAttributes…)开启 Lombok 模型并设置Lombok注解自3.5.10开始. 使用@Data示例: enableLombok(new ClassAnnotationAttributes(“@Data”,“lombok.Data”))

当然了除了上面提到的全局配置和策略配置,还有数据源配置,包名配置等等,但这些都比较简单,顾名思义数据源配置就是配置数据源的,包名配置就是配置生成代码类的包名路径,具体方法api还是请移步官网查看

4. 自定义模板:打造团队专属代码风格

4.1 自定义DTO生成模板

MPG默认不提供DTO模板,但我们可以轻松扩展。创建templates/entityDTO.java.ftl

package ${package.Entity};
​
<#list importEntityFrameworkPackages as pkg>
import ${pkg};
</#list>
​
<#list importEntityJavaPackages as pkg>
import ${pkg};
</#list>
​
/**
 * <p>
 * ${table.comment!}
 * </p>
 *
 * @author ${author}
 * @since ${date}
 */
@Data
@Schema(description = "${table.comment!}")
public class ${entity}DTO {
<#-- ----------  BEGIN 字段循环遍历  ---------->
<#list table.fields as field>
    @Schema(description = "${field.comment!}")
    private ${field.propertyType} ${field.propertyName};
</#list>
<#------------  END 字段循环遍历  ---------->
}
​

关于Freemarker模版的语法,请另行查阅资料。

在上面生成示例代码中追加注入配置:

.injectionConfig(injectConfig -> {
                    injectConfig.customFile(new CustomFile.Builder()
                            .fileName("DTO.java") // 文件名称
                            .templatePath("templates/entityDTO.java.ftl") //指定生成模板路径
                            .packageName("model.dto") // 包名,
                            .build());

再次运行就能生成如下代码:

@Data
@Schema(description = "用户信息")
public class UserDTO {
    @Schema(description = "主键")
    private Long id;
    @Schema(description = "编号")
    private String userNo;
    @Schema(description = "昵称")
    private String name;
    @Schema(description = "邮箱")
    private String email;
    @Schema(description = "手机号")
    private String phone;
    @Schema(description = "性别  0:男生   1:女生")
    private Byte gender;
    @Schema(description = "出生日期")
    private LocalDateTime birthday;
    @Schema(description = "删除标志 0:否  1:是")
    private Byte isDelete;
    @Schema(description = "创建时间")
    private LocalDateTime createTime;
    @Schema(description = "更新时间")
    private LocalDateTime updateTime;
    @Schema(description = "创建人")
    private Long creator;
    @Schema(description = "更新人")
    private Long updater;
    @Schema(description = "地址")
    private String address;
    @Schema(description = "角色id")
    private String roleId;
    @Schema(description = "爱好")
    private String hobby;
    @Schema(description = "个人说明")
    private String remark;
    @Schema(description = "公司id")
    private Long orgId;
}
​

4.2 定制功能丰富的Controller模板

默认生成的Controller比较基础,如果业务的CRUD代码没有包含复杂处理逻辑,那么我们可以通过定制代码减少编码重复,创建更实用的模板templates/controller.java.ftl

<#assign serviceNameLower = table.serviceName?uncap_first>
<#assign entityNameLower = table.entityName?uncap_first>
​
package ${package.Controller};
​
import ${package.Service}.${table.serviceName};
import ${package.Param}.${table.entityName}Param;
import ${package.Query}.${table.entityName}Query;
import ${package.VO}.${table.entityName}VO;
import com.plasticene.boot.common.pojo.ResponseVO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
​
​
/**
 * <p>
 * ${table.comment!}
 * </p>
 *
 * @author ${author}
 * @since ${date}
 */
@RestController
@RequestMapping("<#if package.ModuleName?? && package.ModuleName != "">/${package.ModuleName}</#if>/<#if controllerMappingHyphenStyle>${controllerMappingHyphen}<#else>${table.entityPath}</#if>")
@Tag(name = "${table.comment!}")
public class ${table.controllerName} {
    @Resource
    private ${table.serviceName} ${serviceNameLower};
​
    @PostMapping
    @Operation(summary = "创建")
    public ResponseVO<Long> create(@RequestBody @Validated ${table.entityName}Param ${entityNameLower}Param) {
        Long id = ${serviceNameLower}.create(${entityNameLower}Param);
         return ResponseVO.success(id);
    }
​
    @PutMapping
    @Operation(summary = "修改")
    public ResponseVO<Void> update(@RequestBody @Validated ${table.entityName}Param ${entityNameLower}Param) {
        ${serviceNameLower}.update(${entityNameLower}Param);
        return ResponseVO.success();
    }
​
    @DeleteMapping
    @Operation(summary = "删除")
    public ResponseVO<Void> delete(@RequestBody List<Long> idList) {
        ${serviceNameLower}.delete(idList);
        return ResponseVO.success();
    }
​
    @GetMapping("/page")
    @Operation(summary = "分页")
    public ResponseVO<PageResult<${table.entityName}VO>> page(${table.entityName}Query ${entityNameLower}Quey) {
        PageResult<${table.entityName}VO> pageVO = ${serviceNameLower}.page(${entityNameLower}Query);
        return ResponseVO.success(pageVO);
    }
​
    @GetMapping("/{id}")
    @Operation(summary = "详情")
    public ResponseVO<${table.entityName}VO> detail(@PathVariable("id") Long id) {
        ${table.entityName}VO ${entityNameLower}VO = ${serviceNameLower}.detail(id);
        return ResponseVO.success(${entityNameLower}VO);
    }
​
}

再次运行上面生成示例代码:UserController

package com.shepherd.example.controller;
​
import com.shepherd.example.service.UserService;
import com.shepherd.example.model.param.UserParam;
import com.shepherd.example.model.query.UserQuery;
import com.shepherd.example.model.vo.UserVO;
import com.plasticene.boot.common.pojo.ResponseVO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
​
​
/**
 * <p>
 * 用户信息
 * </p>
 *
 * @author ZFJ
 * @since 2025-12-03
 */
@RestController
@RequestMapping("/user")
@Tag(name = "用户信息")
public class UserController {
    @Resource
    private UserService userService;
​
    @PostMapping
    @Operation(summary = "创建")
    public ResponseVO<Long> create(@RequestBody @Validated UserParam userParam) {
        Long id = userService.create(userParam);
         return ResponseVO.success(id);
    }
​
    @PutMapping
    @Operation(summary = "修改")
    public ResponseVO<Void> update(@RequestBody @Validated UserParam userParam) {
        userService.update(userParam);
        return ResponseVO.success();
    }
​
    @DeleteMapping
    @Operation(summary = "删除")
    public ResponseVO<Void> delete(@RequestBody List<Long> idList) {
        userService.delete(idList);
        return ResponseVO.success();
    }
​
    @GetMapping("/page")
    @Operation(summary = "分页")
    public ResponseVO<PageResult<UserVO>> page(UserQuery userQuey) {
        PageResult<UserVO> pageVO = userService.page(userQuery);
        return ResponseVO.success(pageVO);
    }
​
    @GetMapping("/{id}")
    @Operation(summary = "详情")
    public ResponseVO<UserVO> detail(@PathVariable("id") Long id) {
        UserVO userVO = userService.detail(id);
        return ResponseVO.success(userVO);
    }
​
}
​

至于service层的业务定制代码,大家自行尝试定制一下哈。

5.深入原理:MPG是如何工作的?

5.1 核心类结构

AutoGenerator              // 代码生成器入口
├── ConfigBuilder          // 配置构建器
├── TemplateEngine         // 模板引擎抽象
│   ├── VelocityTemplateEngine
│   ├── FreemarkerTemplateEngine
│   └── BeetlTemplateEngine
└── InjectionConfig        // 注入配置

5.2 代码生成流程

  1. 配置解析:解析全局配置、数据源配置、包配置、策略配置
  2. 表信息获取:通过JDBC获取数据库表结构和字段信息
  3. 模板渲染:使用模板引擎将数据模型渲染到模板文件中
  4. 文件输出:将渲染结果写入到指定目录

5.3 关键源码分析

调试上面生成代码示例,直接来到AutoGenerator的执行方法#execute()

 public void execute(AbstractTemplateEngine templateEngine) {
        logger.debug("==========================准备生成文件...==========================");
        
        // 如果配置信息为空,则创建新的配置构建器
        if (null == this.config) {
            this.config = new ConfigBuilder(this.packageInfo, this.dataSource, this.strategy, this.template, this.globalConfig, this.injection);
        }
​
        // 如果模板引擎为空,则使用默认的Velocity模板引擎
        if (null == templateEngine) {
            templateEngine = new VelocityTemplateEngine();
        }
​
        // 设置配置构建器并执行批量输出操作
        templateEngine.setConfigBuilder(this.config);
        templateEngine.init(this.config).batchOutput().open();
        logger.debug("==========================文件生成完成!!!==========================");
    }

最后到生成各类代码文件的方法batchOutput()

    /**
     * 批量输出模板文件
     * <p>
     * 该方法根据配置信息批量生成代码文件,包括实体类、Mapper、Service、Controller等文件。
     * 处理流程如下:
     * 1. 获取配置构建器和表信息列表
     * 2. 遍历每张表,生成对应的对象映射关系
     * 3. 执行自定义文件输出前的回调处理
     * 4. 输出各类代码文件
     *
     * @return AbstractTemplateEngine 模板引擎实例,用于链式调用
     * @throws RuntimeException 当文件创建失败或配置信息有误时抛出运行时异常
     */
    public @NotNull 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);
                    // 输出自定义文件
                    this.outputCustomFile(t.getCustomFiles(), tableInfo, objectMap);
                });
                
                // 按顺序输出各类标准代码文件
                this.outputEntity(tableInfo, objectMap);
                this.outputMapper(tableInfo, objectMap);
                this.outputService(tableInfo, objectMap);
                this.outputController(tableInfo, objectMap);
            });
            
            return this;
        } catch (Exception e) {
            throw new RuntimeException("无法创建文件,请检查配置信息!", e);
        }
    }
​

6.总结

MyBatis-Plus代码生成器作为一款功能强大、扩展性极佳的代码生成工具,通过合理的配置策略和灵活的自定义模板,能够满足绝大多数项目的代码生成需求。深入理解其底层原理并掌握高级应用技巧,不仅能大幅提升开发效率,更能确保团队代码的规范性和一致性。

对于后端开发者而言,定制符合团队规范的代码生成方案,主要有两种实践路径:

📦 方案一:Spring Boot深度集成

  • 将生成策略封装为配置文件,与项目无缝集成
  • 支持一键生成代码到指定项目目录
  • 可进一步封装为Starter,实现团队级标准化

🔧 方案二:工具类灵活定制

  • 提供高度可配置的代码生成工具类
  • 支持运行时动态规则设置
  • 更适合多项目、差异化的生成需求

💡 重要提醒

需要特别说明的是,本文旨在系统介绍MyBatis-Plus代码生成器的技术实现与最佳实践,并非鼓励盲目依赖代码生成。在实际项目中是否采用、如何采用,还需要结合具体的业务场景、团队规范和技术架构来审慎决策。

关键在于:工具是为了提升效率,而好的代码更需要开发者的设计思维和业务理解。希望本文能帮助你做出更明智的技术选型!


欢迎在评论区分享你的代码生成器使用经验!如果你有更好的定制方案,也期待一起交流探讨~

觉得文章有帮助的话,欢迎点赞、收藏、转发支持~