跳到主要内容

代码生成器

代码生成器是 MyBatis Plus 最实用的功能之一,它可以快速生成 Entity、Mapper、Service、Controller 等模块代码,大幅提升开发效率。本章将详细介绍代码生成器的使用方法和配置技巧。

什么是代码生成器?

在企业级项目开发中,我们需要为每张数据库表创建对应的实体类、Mapper 接口、Service 层和 Controller 层代码。这些代码结构相似、模式固定,手写既耗时又容易出错。MyBatis Plus 的代码生成器通过读取数据库表结构,自动生成这些标准化的代码,让开发者将精力集中在业务逻辑上。

代码生成器能为我们节省多少工作量?假设一个项目有 50 张表,每张表需要创建 5 个文件(Entity、Mapper、Mapper XML、Service、Controller),手写需要 250 个文件。使用代码生成器,只需几分钟就能完成。

安装依赖

添加 Maven 依赖

代码生成器是一个独立的模块,需要单独引入:

<dependencies>
<!-- MyBatis Plus 代码生成器 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.5.5</version>
</dependency>

<!-- 模板引擎(以下任选其一) -->
<!-- Velocity(默认) -->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.3</version>
</dependency>

<!-- 或者使用 Freemarker -->
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.32</version>
</dependency>

<!-- 或者使用 Beetl -->
<dependency>
<groupId>com.ibeetl</groupId>
<artifactId>beetl</artifactId>
<version>3.15.10.RELEASE</version>
</dependency>
</dependencies>

模板引擎选择

MyBatis Plus 支持以下模板引擎:

模板引擎特点推荐场景
Velocity语法简单,官方默认快速上手,标准项目
Freemarker功能强大,社区活跃需要复杂模板逻辑
Beetl性能优异,语法接近 Java追求高性能
Enjoy轻量级,JFinal 同款简单场景

如果不指定模板引擎,默认使用 Velocity。本文档使用 Freemarker 作为示例。

快速开始

最简配置

下面的代码演示了代码生成器的最简配置,只需几行代码就能生成完整的 CRUD 代码:

import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import java.nio.file.Paths;

public class CodeGenerator {
public static void main(String[] args) {
FastAutoGenerator.create("jdbc:mysql://localhost:3306/mydb", "root", "123456")
.globalConfig(builder -> builder
.author("张三")
.outputDir(Paths.get(System.getProperty("user.dir")) + "/src/main/java")
)
.packageConfig(builder -> builder
.parent("com.example.demo")
)
.strategyConfig(builder -> builder
.addInclude("t_user") // 设置要生成的表名
.entityBuilder().enableLombok()
)
.templateEngine(new FreemarkerTemplateEngine())
.execute();
}
}

运行上述代码后,会在指定目录生成以下文件:

src/main/java/com/example/demo/
├── entity/
│ └── User.java # 实体类
├── mapper/
│ └── UserMapper.java # Mapper 接口
├── service/
│ ├── UserService.java # Service 接口
│ └── impl/
│ └── UserServiceImpl.java # Service 实现
└── controller/
└── UserController.java # Controller

理解生成流程

代码生成器的工作流程分为三个主要阶段:

第一阶段:读取数据库元数据

生成器连接数据库,读取表结构信息,包括表名、字段名、字段类型、注释、主键等元数据。这些信息将用于后续的代码生成。

第二阶段:应用配置规则

根据配置的全局配置、包配置、策略配置等规则,对元数据进行转换。例如,将数据库的 user_name 字段转换为 Java 的 userName 属性。

第三阶段:渲染模板输出

使用模板引擎渲染预定义的模板,生成最终的代码文件。每个类型的文件对应一个模板。

详细配置

全局配置(GlobalConfig)

全局配置控制生成代码的整体行为:

.globalConfig(builder -> builder
// 作者名称,会添加到类注释中
.author("张三")

// 输出目录,生成的代码会放在这个目录下
.outputDir(Paths.get(System.getProperty("user.dir")) + "/src/main/java")

// 是否打开输出目录(生成后自动打开文件夹)
.openDir(false)

// 是否覆盖已存在文件
.fileOverride(true)

// 开启 Swagger2 模式,生成 Swagger 注解
.enableSwagger()

// 时间类型策略(Java 8 时间 API)
.dateType(DateType.TIME_PACK)

// 注释日期格式
.commentDate("yyyy-MM-dd")
)

关键配置项说明

outputDir:代码输出目录。建议使用 System.getProperty("user.dir") 获取项目根目录,然后拼接相对路径,这样在不同环境下都能正常工作。

fileOverride:是否覆盖已有文件。首次生成设为 true,后续如果修改了生成的代码,应设为 false 避免覆盖。

enableSwagger:开启 Swagger 模式后,实体类会添加 @ApiModel@ApiModelProperty 注解,方便生成 API 文档。

dateType:日期类型策略。DateType.ONLY_DATE 使用 java.util.DateDateType.TIME_PACK 使用 Java 8 的 LocalDateTime 等时间 API,推荐使用后者。

数据源配置(DataSourceConfig)

数据源配置定义数据库连接信息:

.dataSourceConfig(builder -> builder
// 数据库连接 URL
.url("jdbc:mysql://localhost:3306/mydb?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai")

// 数据库用户名
.username("root")

// 数据库密码
.password("123456")

// 数据库驱动类名(MySQL 8.x 推荐使用新驱动)
.driverClassName("com.mysql.cj.jdbc.Driver")

// 类型转换器(处理数据库类型到 Java 类型的映射)
.typeConvertHandler((globalConfig, typeRegistry, metaInfo) -> {
// 自定义类型转换:SMALLINT 转 Integer
if (metaInfo.getJdbcType().TYPE_CODE == Types.SMALLINT) {
return DbColumnType.INTEGER;
}
return typeRegistry.getColumnType(metaInfo);
})
)

MySQL 连接 URL 常用参数

jdbc:mysql://localhost:3306/mydb?
useUnicode=true # 使用 Unicode 编码
&characterEncoding=utf-8 # 字符编码
&serverTimezone=Asia/Shanghai # 时区设置
&useSSL=false # 禁用 SSL(开发环境)
&allowPublicKeyRetrieval=true # 允许获取公钥
&tinyInt1isBit=true # tinyint(1) 转 Boolean
&remarks=true # 读取表注释
&useInformationSchema=true # 使用信息模式读取元数据

最后一个参数 remarks=true&useInformationSchema=true 很重要,它能确保代码生成器正确读取表和字段的注释信息。

包配置(PackageConfig)

包配置定义生成代码的包结构:

.packageConfig(builder -> builder
// 父包名,生成的代码都会在这个包下
.parent("com.example.demo")

// 模块名,会拼接到父包名后面
.moduleName("system")

// Entity 包名
.entity("entity")

// Mapper 接口包名
.mapper("mapper")

// Service 接口包名
.service("service")

// Service 实现类包名
.serviceImpl("service.impl")

// Controller 包名
.controller("controller")

// Mapper XML 文件包名(相对于 resources 目录)
.xml("mapper")

// 自定义路径配置
.pathInfo(Collections.singletonMap(
OutputFile.xml,
Paths.get(System.getProperty("user.dir")) + "/src/main/resources/mapper"
))
)

生成的包结构

上述配置生成的包结构如下:

com.example.demo.system
├── entity/ # 实体类
├── mapper/ # Mapper 接口
├── service/ # Service 接口
│ └── impl/ # Service 实现
└── controller/ # Controller

resources/
└── mapper/ # Mapper XML

策略配置(StrategyConfig)

策略配置是最重要的配置项,它控制生成代码的具体内容:

.strategyConfig(builder -> builder
// 设置要生成的表名,支持通配符
.addInclude("t_user", "t_order", "t_product")

// 设置过滤表前缀,生成的实体类会去掉这些前缀
.addTablePrefix("t_", "tb_")

// ==================== Entity 策略 ====================
.entityBuilder()
// 启用 Lombok,生成 @Data、@Getter 等注解
.enableLombok()

// 启用链式模型,生成 setter 返回 this
.enableChainModel()

// 启用 ActiveRecord 模式
.enableActiveRecord()

// 逻辑删除字段名
.logicDeleteColumnName("deleted")

// 乐观锁字段名
.versionColumnName("version")

// 表字段填充策略
.addTableFills(
new Column("create_time", FieldFill.INSERT),
new Column("update_time", FieldFill.INSERT_UPDATE)
)

// 格式化文件名称,%s 表示表名
.formatFileName("%s")

// ==================== Mapper 策略 ====================
.mapperBuilder()
// 设置父类 Mapper
.superClass(BaseMapper.class)

// 启用 @Mapper 注解
.enableMapperAnnotation()

// 格式化 Mapper 文件名
.formatMapperFileName("%sMapper")

// 格式化 XML 文件名
.formatXmlFileName("%sMapper")

// ==================== Service 策略 ====================
.serviceBuilder()
// 设置 Service 接口父类
.superServiceClass(IService.class)

// 设置 Service 实现类父类
.superServiceImplClass(ServiceImpl.class)

// 格式化 Service 接口文件名
.formatServiceFileName("%sService")

// 格式化 Service 实现类文件名
.formatServiceImplFileName("%sServiceImpl")

// ==================== Controller 策略 ====================
.controllerBuilder()
// 启用 REST 风格
.enableRestStyle()

// 启用驼峰转连字符
.enableHyphenStyle()

// 设置父类 Controller
.superClass(BaseController.class)

// 格式化文件名
.formatFileName("%sController")
)

策略配置详解

表名过滤addInclude 指定要生成的表,addTablePrefix 指定要移除的表前缀。例如,表名 t_user_info,前缀 t_,生成的实体类名为 UserInfo

Lombok 支持:启用后,实体类不再生成 getter/setter 方法,而是添加 @Data 注解。这让代码更简洁,推荐开启。

链式模型:启用后,setter 方法返回 this,支持链式调用:user.setName("张三").setAge(25).setEmail("[email protected]")

ActiveRecord 模式:启用后,实体类继承 Model,可以直接调用 CRUD 方法:user.insert()user.deleteById()

逻辑删除和乐观锁:指定字段名后,生成器会自动添加 @TableLogic@Version 注解。

完整示例

企业级配置示例

下面是一个完整的企业级代码生成器配置,涵盖了实际项目中常用的各种配置:

import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.fill.Column;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import org.apache.ibatis.annotations.Mapper;

import java.nio.file.Paths;
import java.util.Collections;
import java.util.Scanner;

public class CodeGenerator {

public static void main(String[] args) {
// 数据库连接信息
String url = "jdbc:mysql://localhost:3306/mydb?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&useInformationSchema=true&remarks=true";
String username = "root";
String password = "123456";

// 项目路径
String projectPath = System.getProperty("user.dir");

FastAutoGenerator.create(url, username, password)
// ==================== 全局配置 ====================
.globalConfig(builder -> builder
.author("developer")
.outputDir(projectPath + "/src/main/java")
.openDir(false)
.fileOverride(true)
.enableSwagger()
.dateType(DateType.TIME_PACK)
.commentDate("yyyy-MM-dd HH:mm:ss")
)
// ==================== 包配置 ====================
.packageConfig(builder -> builder
.parent("com.example")
.moduleName("business")
.entity("entity")
.mapper("mapper")
.service("service")
.serviceImpl("service.impl")
.controller("controller")
.pathInfo(Collections.singletonMap(
OutputFile.xml,
projectPath + "/src/main/resources/mapper"
))
)
// ==================== 策略配置 ====================
.strategyConfig(builder -> builder
// 指定生成的表,使用交互式输入
.addInclude(getTables("t_user,t_order,t_product"))

// 过滤表前缀
.addTablePrefix("t_", "tb_", "sys_")

// Entity 策略
.entityBuilder()
.naming(NamingStrategy.underline_to_camel)
.columnNaming(NamingStrategy.underline_to_camel)
.idType(IdType.AUTO)
.enableLombok()
.enableChainModel()
.enableTableFieldAnnotation()
.logicDeleteColumnName("deleted")
.versionColumnName("version")
.addTableFills(
new Column("create_time", FieldFill.INSERT),
new Column("update_time", FieldFill.INSERT_UPDATE),
new Column("create_by", FieldFill.INSERT),
new Column("update_by", FieldFill.INSERT_UPDATE)
)
.formatFileName("%s")

// Mapper 策略
.mapperBuilder()
.superClass(BaseMapper.class)
.enableMapperAnnotation()
.enableBaseResultMap()
.enableBaseColumnList()
.formatMapperFileName("%sMapper")
.formatXmlFileName("%sMapper")

// Service 策略
.serviceBuilder()
.formatServiceFileName("%sService")
.formatServiceImplFileName("%sServiceImpl")

// Controller 策略
.controllerBuilder()
.enableRestStyle()
.enableHyphenStyle()
.formatFileName("%sController")
)
// 使用 Freemarker 模板引擎
.templateEngine(new FreemarkerTemplateEngine())
.execute();

System.out.println("代码生成完成!");
}

/**
* 处理表名输入
* 支持逗号分隔多个表名
*/
protected static String[] getTables(String tables) {
if (tables == null || tables.isEmpty()) {
return new String[0];
}
return tables.split(",");
}
}

交互式生成

如果希望每次运行时动态输入表名等参数,可以使用交互式生成:

public class InteractiveCodeGenerator {

private static final Scanner SCANNER = new Scanner(System.in);

public static void main(String[] args) {
FastAutoGenerator.create("jdbc:mysql://localhost:3306/mydb", "root", "123456")
// 全局配置 - 交互式输入作者
.globalConfig((scanner, builder) ->
builder.author(scanner.apply("请输入作者名称:")))

// 包配置 - 交互式输入包名
.packageConfig((scanner, builder) ->
builder.parent(scanner.apply("请输入父包名(如 com.example):")))

// 策略配置 - 交互式输入表名
.strategyConfig((scanner, builder) ->
builder.addInclude(getTables(scanner.apply("请输入表名,多个用逗号分隔,输入 all 生成全部:")))
.entityBuilder().enableLombok())

.templateEngine(new FreemarkerTemplateEngine())
.execute();
}

protected static List<String> getTables(String tables) {
if ("all".equalsIgnoreCase(tables)) {
return Collections.emptyList(); // 空列表表示生成全部表
}
return Arrays.asList(tables.split(","));
}
}

自定义模板

当内置模板不能满足需求时,可以自定义模板。

使用自定义模板文件

.strategyConfig(builder -> builder
.entityBuilder()
// 指定自定义实体模板
.template("/templates/entity.java")
.mapperBuilder()
// 指定自定义 Mapper 模板
.template("/templates/mapper.java")
// 指定自定义 XML 模板
.templateXml("/templates/mapper.xml")
.serviceBuilder()
// 指定自定义 Service 模板
.template("/templates/service.java")
.templateImpl("/templates/serviceImpl.java")
.controllerBuilder()
// 指定自定义 Controller 模板
.template("/templates/controller.java")
)

模板变量

在自定义模板中,可以使用以下常用变量:

变量说明示例
${package}包名com.example.entity
${author}作者张三
${date}日期2024-01-01
${tableName}表名t_user
${entity}实体类名User
${table.comment}表注释用户表
${table.fields}字段列表-

最佳实践

项目结构建议

project/
├── src/main/
│ ├── java/
│ │ └── com/example/
│ │ ├── business/ # 业务模块
│ │ │ ├── entity/ # 实体类
│ │ │ ├── mapper/ # Mapper 接口
│ │ │ ├── service/ # Service 层
│ │ │ │ └── impl/
│ │ │ └── controller/ # Controller
│ │ ├── common/ # 公共模块
│ │ │ ├── base/ # 基类
│ │ │ └── config/ # 配置类
│ │ └── generator/ # 代码生成器
│ │ └── CodeGenerator.java
│ └── resources/
│ └── mapper/ # Mapper XML
└── pom.xml

开发建议

生成前先规划:确定好包结构、命名规范后再生成代码,避免反复修改配置。

分批生成:大型项目建议按模块分批生成,便于管理和调试。

保留修改:如果手动修改了生成的代码,后续重新生成时注意不要覆盖。

版本控制:将代码生成器纳入版本控制,方便团队成员使用。

模板定制:根据团队规范定制模板,统一代码风格。

常见问题

问题 1:表注释读取为空

原因:MySQL 默认不读取表注释。

解决:在连接 URL 中添加 useInformationSchema=true&remarks=true 参数。

问题 2:生成后编译报错

原因:可能是依赖缺失或版本不兼容。

解决:确保引入了必要的依赖(Lombok、Swagger 等),检查 MyBatis Plus 和 Spring Boot 版本兼容性。

问题 3:字段类型映射不正确

原因:默认的类型转换器可能无法识别某些数据库类型。

解决:使用 typeConvertHandler 自定义类型转换逻辑。

.dataSourceConfig(builder -> builder
.typeConvertHandler((globalConfig, typeRegistry, metaInfo) -> {
// MySQL 的 tinyint(1) 转 Boolean
if (metaInfo.getJdbcType() == JdbcType.TINYINT
&& metaInfo.getColumnSize() == 1) {
return DbColumnType.BOOLEAN;
}
// MySQL 的 tinyint 转 Integer
if (metaInfo.getJdbcType() == JdbcType.TINYINT) {
return DbColumnType.INTEGER;
}
return typeRegistry.getColumnType(metaInfo);
})
)

小结

本章我们学习了:

  1. 代码生成器基础:理解代码生成器的作用和价值
  2. 安装配置:引入依赖、选择模板引擎
  3. 快速开始:最简配置示例
  4. 详细配置:全局配置、数据源配置、包配置、策略配置
  5. 自定义模板:满足特殊需求
  6. 最佳实践:项目结构和开发建议

代码生成器能大幅提升开发效率,但也要注意合理使用。生成的代码是起点而非终点,后续还需要根据业务需求进行调整和完善。

参考资源