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

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

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

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

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

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

2.1 引入依赖

xml 复制代码
  <!-- 代码生成器  -->
  <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:

sql 复制代码
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,让配置过程直观易懂:

scss 复制代码
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 实体策略配置
controllerBuilder Controller 策略配置
mapperBuilder Mapper 策略配置
serviceBuilder Service 策略配置

可以看到策略配置对提供统一的生成规则配置,还支持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

bash 复制代码
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模版的语法,请另行查阅资料。

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

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

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

ini 复制代码
@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

less 复制代码
<#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

less 复制代码
​
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 核心类结构

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

5.2 代码生成流程

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

5.3 关键源码分析

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

kotlin 复制代码
 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()

kotlin 复制代码
    /**
     * 批量输出模板文件
     * <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代码生成器的技术实现与最佳实践,并非鼓励盲目依赖代码生成。在实际项目中是否采用、如何采用,还需要结合具体的业务场景、团队规范和技术架构来审慎决策。

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


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

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

相关推荐
申阳1 小时前
Day 20:开源个人项目时的一些注意事项
前端·后端·程序员
sivdead1 小时前
Agent平台消息节点输出设计思路
后端·python·agent
程序员西西1 小时前
作为开发,你真的懂 OOM 吗?实测 3 种场景,搞懂 JVM 崩溃真相
java·后端
小周在成长1 小时前
Java 内部类指南
后端
橘子编程1 小时前
仓颉语言变量与表达式解析
java·linux·服务器·开发语言·数据库·python·mysql
开心就好20251 小时前
Fiddler抓包与接口调试实战,HTTPHTTPS配置、代理设置与移动端抓包详解
后端
u***28471 小时前
Spring Boot项目接收前端参数的11种方式
前端·spring boot·后端
古城小栈1 小时前
SpringBoot项目集成第三方CAS-client jar包
spring boot·后端·jar
pcm1235671 小时前
java中用哈希表写题碰到的误区
java·前端·散列表