1. MyBatis-Plus代码生成器概述
MyBatis-Plus作为MyBatis的增强工具,其代码生成器功能一直是开发者最喜爱的特性之一。新版代码生成器在原有基础上进行了全面升级,支持更灵活的配置方式和更丰富的自定义选项。在实际项目开发中,它能帮助我们快速生成Entity、Mapper、Service、Controller等基础代码,将重复性的CRUD操作代码生成时间从小时级缩短到分钟级。
我最近在一个电商后台管理系统项目中使用了3.5.3版本的代码生成器,相比旧版,新版本在模板自定义、字段注解生成等方面有了明显改进。通过合理配置,我们甚至可以实现DDD分层架构的代码自动生成,这在以前需要大量手工修改才能实现。
2. 环境准备与基础配置
2.1 依赖引入
首先需要在项目中引入MyBatis-Plus生成器模块。对于Maven项目,在pom.xml中添加以下依赖:
xml复制<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.5.3</version>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.31</version>
</dependency>
注意:新版生成器默认使用Freemarker作为模板引擎,因此需要额外引入freemarker依赖。如果项目中使用的是Velocity,需要对应引入velocity-engine-core依赖。
2.2 数据库连接配置
代码生成器需要连接数据库获取表结构信息,推荐使用HikariCP作为连接池:
java复制// 数据源配置
DataSourceConfig.Builder dataSourceConfig = new DataSourceConfig.Builder(
"jdbc:mysql://localhost:3306/your_db",
"username",
"password"
);
dataSourceConfig.typeConvert(new MySqlTypeConvert());
dataSourceConfig.schema("your_schema");
在实际项目中,我建议将这些配置提取到properties文件中,通过@Value注入,避免硬编码。
3. 生成器核心配置详解
3.1 全局配置
全局配置决定了生成文件的输出位置、作者信息等基础设置:
java复制GlobalConfig.Builder globalConfig = new GlobalConfig.Builder();
globalConfig.outputDir(System.getProperty("user.dir") + "/src/main/java");
globalConfig.author("YourName");
globalConfig.openDir(false); // 生成后不打开文件夹
globalConfig.dateType(DateType.TIME_PACK); // 使用java.time包下的时间类
globalConfig.commentDate("yyyy-MM-dd");
globalConfig.enableSwagger(); // 启用Swagger注解
经验分享:设置openDir(false)可以避免在CI/CD环境中生成代码时弹出文件夹。dateType建议使用TIME_PACK,因为java.time包是JDK8后推荐的时间处理方式。
3.2 包配置
包配置决定了生成的代码放在哪个包下:
java复制PackageConfig.Builder packageConfig = new PackageConfig.Builder();
packageConfig.parent("com.example.demo");
packageConfig.moduleName("system"); // 模块名,会作为子包
packageConfig.entity("entity");
packageConfig.mapper("mapper");
packageConfig.service("service");
packageConfig.serviceImpl("service.impl");
packageConfig.controller("controller");
在实际项目中,我通常会根据业务模块划分不同的packageConfig。例如用户模块和订单模块使用不同的moduleName,这样可以更好地组织代码结构。
3.3 策略配置
策略配置是最复杂的部分,也是最能体现新版生成器强大之处的地方:
java复制StrategyConfig.Builder strategyConfig = new StrategyConfig.Builder();
strategyConfig.addInclude("user", "order"); // 只生成这两张表
strategyConfig.addTablePrefix("t_"); // 忽略表前缀
// 实体类策略
strategyConfig.entityBuilder()
.enableLombok() // 使用Lombok
.enableChainModel() // 链式模型
.enableRemoveIsPrefix() // 移除is前缀
.versionColumnName("version") // 乐观锁字段名
.logicDeleteColumnName("deleted") // 逻辑删除字段名
.naming(NamingStrategy.underline_to_camel) // 下划线转驼峰
.columnNaming(NamingStrategy.underline_to_camel)
.addTableFills(new Column("create_time", FieldFill.INSERT)) // 自动填充
.addTableFills(new Column("update_time", FieldFill.INSERT_UPDATE))
.idType(IdType.AUTO); // 主键策略
// Controller策略
strategyConfig.controllerBuilder()
.enableHyphenStyle() // 使用连字符
.enableRestStyle(); // 使用@RestController
// Mapper策略
strategyConfig.mapperBuilder()
.enableMapperAnnotation() // 使用@Mapper
.enableBaseResultMap() // 生成resultMap
.enableBaseColumnList(); // 生成columnList
新版生成器的策略配置采用了Builder模式,比旧版的XML配置更加直观和灵活。特别是字段填充策略,在实际开发中非常实用,可以自动处理创建时间、更新时间等通用字段。
4. 自定义模板与高级功能
4.1 自定义模板
新版生成器允许完全自定义模板文件。默认情况下,生成器会使用内置的Freemarker模板,但我们可以通过以下方式覆盖:
java复制TemplateConfig.Builder templateConfig = new TemplateConfig.Builder();
templateConfig.entity("/templates/entity.java");
templateConfig.mapper("/templates/mapper.java");
templateConfig.service("/templates/service.java");
templateConfig.serviceImpl("/templates/serviceImpl.java");
templateConfig.controller("/templates/controller.java");
在resources/templates目录下创建对应的模板文件即可。模板中使用Freemarker语法,可以访问到表结构、字段等各种上下文变量。
我在项目中自定义了一个entity模板,自动添加了Swagger注解和字段校验注解:
ftl复制package ${package.Entity};
<#list table.importPackages as pkg>
import ${pkg};
</#list>
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import javax.validation.constraints.*;
<#if entityLombokModel>
import lombok.Data;
import lombok.EqualsAndHashCode;
</#if>
@ApiModel(value = "${table.comment!}", description = "${table.comment!}")
<#if entityLombokModel>
@Data
<#if superEntityClass??>
@EqualsAndHashCode(callSuper = true)
<#else>
@EqualsAndHashCode(callSuper = false)
</#if>
</#if>
public class ${entity} {
<#list table.fields as field>
@ApiModelProperty(value = "${field.comment!}")
<#if field.propertyType == "String">
@NotBlank(message = "${field.comment!}不能为空")
<#elseif field.propertyType == "Integer" || field.propertyType == "Long">
@NotNull(message = "${field.comment!}不能为空")
</#if>
private ${field.propertyType} ${field.propertyName};
</#list>
}
4.2 自定义文件输出
除了默认的Entity、Mapper等文件,我们还可以生成其他类型的文件。例如生成DTO、VO等:
java复制InjectionConfig injectionConfig = new InjectionConfig.Builder()
.beforeOutputFile((tableInfo, objectMap) -> {
// 生成DTO
String dtoPath = "/src/main/java/com/example/demo/dto/";
File dtoFile = new File(dtoPath + tableInfo.getEntityName() + "DTO.java");
// 使用Freemarker渲染DTO模板
// ...
})
.build();
这个功能特别适合DDD架构的项目,可以一次性生成领域层、应用层和接口层的所有基础代码。
5. 完整代码示例与执行
5.1 完整配置类
下面是一个完整的代码生成器配置示例:
java复制public class CodeGenerator {
public static void main(String[] args) {
// 数据源配置
DataSourceConfig dataSourceConfig = new DataSourceConfig.Builder(
"jdbc:mysql://localhost:3306/test",
"root",
"password"
).build();
// 全局配置
GlobalConfig globalConfig = new GlobalConfig.Builder()
.outputDir(System.getProperty("user.dir") + "/src/main/java")
.author("YourName")
.openDir(false)
.dateType(DateType.TIME_PACK)
.commentDate("yyyy-MM-dd")
.enableSwagger()
.build();
// 包配置
PackageConfig packageConfig = new PackageConfig.Builder()
.parent("com.example.demo")
.moduleName("system")
.entity("entity")
.mapper("mapper")
.service("service")
.serviceImpl("service.impl")
.controller("controller")
.build();
// 策略配置
StrategyConfig strategyConfig = new StrategyConfig.Builder()
.addInclude("user", "role")
.addTablePrefix("t_")
.entityBuilder()
.enableLombok()
.enableChainModel()
.naming(NamingStrategy.underline_to_camel)
.columnNaming(NamingStrategy.underline_to_camel)
.addTableFills(new Column("create_time", FieldFill.INSERT))
.addTableFills(new Column("update_time", FieldFill.INSERT_UPDATE))
.idType(IdType.AUTO)
.build()
.controllerBuilder()
.enableHyphenStyle()
.enableRestStyle()
.build()
.mapperBuilder()
.enableMapperAnnotation()
.enableBaseResultMap()
.enableBaseColumnList()
.build()
.build();
// 模板配置
TemplateConfig templateConfig = new TemplateConfig.Builder()
.entity("/templates/entity.java")
.build();
// 执行生成
AutoGenerator autoGenerator = new AutoGenerator(dataSourceConfig);
autoGenerator.global(globalConfig)
.packageInfo(packageConfig)
.strategy(strategyConfig)
.template(templateConfig)
.execute();
}
}
5.2 生成结果示例
执行上述代码后,会生成如下结构的代码:
code复制com.example.demo.system
├── controller
│ └── UserController.java
├── entity
│ └── User.java
├── mapper
│ ├── UserMapper.java
│ └── UserMapper.xml
└── service
├── impl
│ └── UserServiceImpl.java
└── UserService.java
生成的User实体类会包含Lombok注解、Swagger注解和字段校验注解,Controller类会带有@RestController注解,并支持RESTful风格。
6. 常见问题与解决方案
6.1 表字段类型映射不正确
新版生成器提供了TypeConvert接口来处理数据库类型到Java类型的映射。如果发现某些字段类型映射不符合预期,可以自定义类型转换器:
java复制public class CustomMySqlTypeConvert implements ITypeConvert {
@Override
public Class<?> processTypeConvert(GlobalConfig globalConfig, String fieldType) {
if (fieldType.toLowerCase().contains("tinyint(1)")) {
return Boolean.class; // 将tinyint(1)映射为Boolean
}
return new MySqlTypeConvert().processTypeConvert(globalConfig, fieldType);
}
}
然后在数据源配置中指定自定义的类型转换器:
java复制dataSourceConfig.typeConvert(new CustomMySqlTypeConvert());
6.2 生成的代码不符合项目规范
如果生成的代码风格与项目现有规范不一致,可以通过以下方式调整:
- 自定义模板文件,按照项目规范编写模板
- 使用策略配置中的naming方法设置命名规则
- 通过entityBuilder().formatFileName()等方法自定义生成的文件名格式
6.3 多模块项目中的代码生成
对于多模块项目,建议:
- 为每个模块创建独立的代码生成器配置类
- 设置不同的outputDir和packageConfig
- 使用addInclude()或addExclude()精确控制每个模块生成的表
例如,用户模块只生成用户相关表:
java复制strategyConfig.addInclude("user", "user_role", "user_profile");
6.4 生成器执行缓慢
当数据库中有大量表时,生成器可能会执行缓慢。解决方法:
- 使用addInclude()明确指定要生成的表,而不是生成所有表
- 在测试数据库上执行生成,而不是生产环境
- 将生成器配置为不打开输出目录(openDir(false))
7. 最佳实践与进阶技巧
7.1 将生成器集成到项目中
虽然代码生成器通常用于开发阶段,但我们可以将其集成到项目中,通过Profile控制:
java复制@Profile("dev")
@Component
public class DevCodeGenerator implements CommandLineRunner {
@Autowired
private DataSource dataSource;
@Value("${mybatis-plus.generator.package}")
private String basePackage;
@Override
public void run(String... args) throws Exception {
if (shouldGenerateCode()) {
new AutoGenerator(createDataSourceConfig())
.global(createGlobalConfig())
.packageInfo(createPackageConfig())
.strategy(createStrategyConfig())
.execute();
}
}
// 其他配置方法...
}
这样在开发环境启动应用时,可以通过特定参数触发代码生成。
7.2 增量生成策略
在实际项目中,我们经常需要在不覆盖已有代码的情况下进行增量生成:
- 对于Entity、Mapper等基础代码,可以设置文件存在时跳过
- 对于Controller等业务代码,可以生成到临时目录,然后手动合并
- 使用版本控制工具比较差异
新版生成器提供了FileOutConfig接口,可以精确控制每个文件的生成行为。
7.3 生成前端代码
通过自定义模板,我们甚至可以生成前端代码。例如生成基于Element UI的Vue组件:
java复制InjectionConfig injectionConfig = new InjectionConfig.Builder()
.beforeOutputFile((tableInfo, objectMap) -> {
// 生成Vue组件
String vuePath = "../web/src/views/";
File vueFile = new File(vuePath + tableInfo.getEntityName() + "List.vue");
// 使用Freemarker渲染Vue模板
// ...
})
.build();
这样就能实现从数据库表到前端组件的全链路代码生成。
7.4 生成测试代码
为了提高代码质量,我们可以配置生成器同时生成单元测试:
java复制TemplateConfig templateConfig = new TemplateConfig.Builder()
.entity("/templates/entity.java")
.controller("/templates/controller.java")
.service("/templates/service.java")
.serviceImpl("/templates/serviceImpl.java")
.mapper("/templates/mapper.java")
.xml("/templates/mapper.xml")
.controllerTest("/templates/controllerTest.java")
.serviceTest("/templates/serviceTest.java")
.build();
测试模板中可以包含基本的CRUD测试用例,为开发人员提供测试起点。