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) | 乐观锁字段名(数据库字段) | versionColumnName 与 versionPropertyName 二选一即可 |
| versionPropertyName(String) | 乐观锁属性名(实体) | versionColumnName 与 versionPropertyName 二选一即可 |
| logicDeleteColumnName(String) | 逻辑删除字段名(数据库字段) | logicDeleteColumnName 与 logicDeletePropertyName 二选一即可 |
| logicDeletePropertyName(String) | 逻辑删除属性名(实体) | logicDeleteColumnName 与 logicDeletePropertyName 二选一即可 |
| 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 代码生成流程
- 配置解析:解析全局配置、数据源配置、包配置、策略配置
- 表信息获取:通过JDBC获取数据库表结构和字段信息
- 模板渲染:使用模板引擎将数据模型渲染到模板文件中
- 文件输出:将渲染结果写入到指定目录
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代码生成器的技术实现与最佳实践,并非鼓励盲目依赖代码生成。在实际项目中是否采用、如何采用,还需要结合具体的业务场景、团队规范和技术架构来审慎决策。
关键在于:工具是为了提升效率,而好的代码更需要开发者的设计思维和业务理解。希望本文能帮助你做出更明智的技术选型!
欢迎在评论区分享你的代码生成器使用经验!如果你有更好的定制方案,也期待一起交流探讨~
觉得文章有帮助的话,欢迎点赞、收藏、转发支持~