1. MyBatis Flex与代码自动生成:解放双手的ORM新选择
最近在重构一个老项目时,我受够了手动编写重复的DAO层代码。当同事推荐MyBatis Flex的代码生成功能时,我最初是怀疑的——毕竟这类工具用不好反而会增加维护成本。但实测两周后,这个轻量级ORM框架的代码生成能力彻底改变了我的开发流程。
MyBatis Flex作为MyBatis的增强版,不仅保留了原生MyBatis的灵活性,还通过内置的代码生成器实现了"约定优于配置"的开发体验。它特别适合需要快速迭代的中小型项目,比如我手头的这个用户管理系统重构。通过自动生成Entity、Mapper、Service等基础代码,我把原本需要3天完成的CRUD模块开发压缩到了半天,而且生成的代码风格统一,完全避免了手写时的低级错误。
2. 环境准备与基础配置
2.1 依赖引入与版本选择
在Spring Boot项目中集成MyBatis Flex代码生成器,首先需要确认依赖版本匹配。我推荐使用以下组合:
<dependency> <groupId>com.mybatis-flex</groupId> <artifactId>mybatis-flex-spring-boot-starter</artifactId> <version>1.2.8</version> </dependency> <dependency> <groupId>com.mybatis-flex</groupId> <artifactId>mybatis-flex-codegen</artifactId> <version>1.2.8</version> <scope>provided</scope> </dependency>这里有个容易踩的坑:代码生成器依赖(mybatis-flex-codegen)应该设置为provided作用域,因为它只在开发阶段使用,不需要打包到生产环境。我曾在测试服务器上部署时,因为忘记设置作用域导致打包文件大了近10MB。
2.2 数据库连接配置
代码生成需要读取数据库元信息,因此需要在application.yml中配置数据源。建议单独为代码生成器创建配置,与主数据源隔离:
codegen: datasource: url: jdbc:mysql://localhost:3306/user_management?useSSL=false&serverTimezone=UTC username: codegen_user password: Gen@2023 driver-class-name: com.mysql.cj.jdbc.Driver注意:生产环境务必使用最小权限账号,我遇到过生成代码的账号被恶意利用导致数据泄露的事故。建议只授予information_schema和业务库的SELECT权限。
3. 代码生成器核心配置详解
3.1 全局策略配置
创建CodeGenerator实例时,有几个关键策略需要特别注意:
GlobalConfig globalConfig = new GlobalConfig(); // 必须设置的工作目录(绝对路径) globalConfig.setOutputDir(System.getProperty("user.dir") + "/src/main/java"); // 开启Swagger注解生成 globalConfig.setSwagger(true); // 设置作者标识(会生成在类注释中) globalConfig.setAuthor("YourName"); // 覆盖已有文件(慎用) globalConfig.setFileOverride(true);我在团队协作时曾因忘记关闭fileOverride导致同事的修改被覆盖。建议新项目开启此选项,已有项目则应该设置为false,或者使用更精细的文件存在检查策略。
3.2 数据源配置实践
数据源配置直接关系到元数据获取的准确性,这里分享一个增强版的配置方式:
DataSourceConfig dataSourceConfig = new DataSourceConfig(); dataSourceConfig.setUrl(environment.getProperty("codegen.datasource.url")); dataSourceConfig.setUsername(environment.getProperty("codegen.datasource.username")); dataSourceConfig.setPassword(environment.getProperty("codegen.datasource.password")); dataSourceConfig.setDriverName(environment.getProperty("codegen.datasource.driver-class-name")); // 添加类型转换器(处理MySQL的tinyint(1)到Boolean的转换) TypeConverters.register(TypeConverts.MYSQL);对于Oracle等大型数据库,可能需要额外配置schema:
dataSourceConfig.setSchema("HR_SCHEMA"); // Oracle专用配置3.3 包路径与模板定制
合理的包结构对项目可维护性至关重要。这是我的推荐配置:
PackageConfig packageConfig = new PackageConfig(); packageConfig.setModuleName("user"); // 业务模块名 packageConfig.setParent("com.example.ums"); // 根包名 // 各层包路径配置 packageConfig.setEntity("domain.entity"); packageConfig.setMapper("repository"); packageConfig.setService("service"); packageConfig.setServiceImpl("service.impl"); packageConfig.setController("web");如果想自定义生成的代码风格,可以扩展Freemarker模板:
TemplateConfig templateConfig = new TemplateConfig(); templateConfig.setEntity("/templates/entity.java.ftl"); templateConfig.setMapper("/templates/mapper.java.ftl"); // 其他模板...我在金融项目中就定制过模板,自动添加了JSR-303校验注解和字段加密注解,节省了大量重复工作。
4. 表级配置与生成策略
4.1 单表生成配置
针对特定表的精细化控制是MyBatis Flex的强项:
StrategyConfig strategyConfig = new StrategyConfig(); strategyConfig .entityBuilder() .enableLombok() // 启用Lombok .enableChainModel() // 链式调用 .logicDeleteColumnName("is_deleted") // 逻辑删除字段 .versionColumnName("version"); // 乐观锁字段 // 包含特定表(支持正则表达式) strategyConfig.addInclude("t_user.*"); // 排除系统表 strategyConfig.addExclude("sys_.*");4.2 字段级策略控制
字段级别的控制可以解决很多实际开发中的痛点:
strategyConfig .entityBuilder() .naming(NamingStrategies.underline_to_camel) // 字段命名策略 .columnNaming(NamingStrategies.underline_to_camel) .addTableFills(new Column("create_time", FieldFill.INSERT)) // 自动填充 .addTableFills(new Column("update_time", FieldFill.INSERT_UPDATE)) .addIgnoreColumns("tenant_id"); // 忽略字段对于枚举类字段,可以配置自动转换:
strategyConfig .entityBuilder() .addEnumFields("gender", "status"); // 自动生成枚举类5. 高级特性与实战技巧
5.1 多数据源生成方案
在微服务架构下,可能需要从多个数据库生成代码。这是我的解决方案:
// 主库生成 generateForDataSource(primaryDataSourceConfig, "com.example.primary"); // 从库生成 generateForDataSource(secondaryDataSourceConfig, "com.example.secondary"); private void generateForDataSource(DataSourceConfig dsConfig, String basePackage) { new CodeGenerator(dsConfig) .globalConfig(globalConfig.clone().setAuthor(currentUser)) .packageConfig(packageConfig.clone().setParent(basePackage)) .strategyConfig(strategyConfig.clone()) .execute(); }5.2 生成后自动注册组件
通过实现GenerationFinishHook接口,可以在生成完成后自动执行操作:
globalConfig.setGenerationFinishHook(new GenerationFinishHook() { @Override public void finish(File outputDir) { // 自动将Mapper注册到Spring MybatisFlexBootstrap.getInstance() .addMapperClasses(findGeneratedMappers(outputDir)); // 发送通知 dingTalkNotifier.notify("代码生成完成"); } });5.3 生成代码的质量控制
为确保生成的代码符合团队规范,我建立了以下检查机制:
- 集成Checkstyle验证代码风格
- 使用ArchUnit验证架构约束
- 生成后自动执行单元测试模板
- 关键方法必须包含JavaDoc
globalConfig.setEntityCommentGenerator(new EntityCommentGenerator() { @Override public void addModelComment(JavaFile javaFile, Table table) { javaFile.addJavaDocLine("/**"); javaFile.addJavaDocLine(" * " + table.getComment()); javaFile.addJavaDocLine(" * @author " + globalConfig.getAuthor()); javaFile.addJavaDocLine(" * @date " + LocalDate.now()); javaFile.addJavaDocLine(" */"); } });6. 常见问题排查手册
6.1 生成失败问题排查
问题现象:执行生成器后没有任何文件输出
排查步骤:
- 检查outputDir路径权限(特别是Linux系统)
- 确认数据源连接正常(测试数据库连通性)
- 查看include/exclude配置是否过滤了所有表
- 检查日志级别是否为DEBUG
LoggerFactory.getLogger(CodeGenerator.class).setLevel(Level.DEBUG);6.2 字段映射异常处理
当遇到字段类型转换问题时,可以注册自定义类型转换器:
TypeConverters.register(new TypeConverter() { @Override public Object convert(Object value, Class<?> targetType) { if (value instanceof Number && targetType == Boolean.class) { return ((Number)value).intValue() > 0; } return null; // 返回null表示不处理 } });6.3 性能优化建议
当数据库表非常多时(超过200张),生成过程可能较慢。可以通过以下方式优化:
- 分模块生成(按业务域划分)
- 使用并行生成(需要线程安全配置)
- 缓存数据库元信息(实现MetaDataCache接口)
globalConfig.setMetaDataCache(new FileMetaDataCache("/tmp/meta_cache"));7. 与前端Flex布局的协同
虽然与CSS的Flex布局没有直接关系,但在全栈开发中,前后端的"Flex"理念可以很好结合。例如:
- 自动生成TypeScript类型定义
- 基于Swagger生成前端API客户端
- 字段注释自动转为前端表单label
我开发了一个插件,可以从MyBatis Flex实体生成Ant Design Pro的Model:
public class AntDesignProGenerator implements GenerationFinishHook { @Override public void finish(File outputDir) { // 解析生成的Java实体 // 转换为TS接口定义 // 输出到前端项目目录 } }这种前后端协同的代码生成,可以确保数据类型定义的一致性,减少联调时的类型错误。