从单表到多模块MyBatis-Plus-Generator 3.5.2 在微服务项目中的高级玩法与避坑指南当你的项目从单体架构演进为微服务体系时那些曾经得心应手的工具链往往会暴露出新的挑战。MyBatis-Plus-Generator作为持久层开发的利器在单体项目中可能只需简单配置就能快速生成CRUD代码但在多模块、多数据源的微服务环境下它的使用姿势需要全面升级。1. 微服务架构下的代码生成困境在典型的Spring Cloud微服务项目中我们通常会遇到以下几个核心问题数据源分散订单服务连接order_db用户服务连接user_db而生成器配置需要动态适配不同数据库模块化拆分生成的Entity应该放在哪个模块API模块还是Service模块表前缀处理各业务线表名可能带有order_、payment_等前缀需要智能过滤DDD分层适配如何让生成的代码符合领域驱动设计的分层规范我曾在一个电商平台重构项目中因为未处理好这些问题导致生成的代码需要大量手工调整。比如支付服务的实体类被错误地生成到了API网关模块又比如物流服务的Mapper XML路径配置错误导致运行时找不到SQL映射。2. 多数据源环境下的生成器配置对于多数据源场景我们需要改造基础的生成器配置类public class MultiDataSourceGenerator { // 数据源配置映射 private static final MapString, DataSourceConfig DS_CONFIGS Map.of( order, buildDataSourceConfig(jdbc:mysql://127.0.0.1:3306/order_db), user, buildDataSourceConfig(jdbc:mysql://127.0.0.1:3306/user_db) ); public static void generate(String moduleName) { DataSourceConfig dataSourceConfig DS_CONFIGS.get(moduleName); if (dataSourceConfig null) { throw new IllegalArgumentException(无效的模块名称); } FastAutoGenerator.create(dataSourceConfig) .globalConfig(builder - { builder.outputDir(getModulePath(moduleName)); }) // 其他配置... .execute(); } private static String getModulePath(String moduleName) { return System.getProperty(user.dir) / moduleName -service/src/main/java; } }关键配置项对比配置项单体项目微服务项目数据源固定单个数据源根据模块动态切换输出路径直接指定src/main/java需要计算子模块的相对路径表过滤简单前缀过滤需要结合业务线定制过滤策略3. 多模块代码分布策略在DDD分层架构中代码应该按照以下原则分布order-service ├── order-api // 接口定义 ├── order-domain // 领域模型 └── order-infra // 基础设施对应的生成器配置需要细化.packageConfig(builder - { builder.parent(com.example.order) .entity(domain.model) // 实体类放在domain层 .mapper(infra.persistence) // Mapper放在基础设施层 .service(domain.service) // 服务接口放在domain层 .controller(interfaces.rest) // 控制器放在接口层 .pathInfo(Collections.singletonMap( OutputFile.xml, getModulePath(order) /src/main/resources/mapper )); })注意在实际项目中建议将Entity和DTO分开生成Entity放在domain模块DTO放在api模块4. 高级策略配置技巧4.1 智能表前缀处理对于不同业务线的表前缀可以采用动态配置.strategyConfig(builder - { // 根据模块自动匹配表前缀 String tablePrefix moduleName _; builder.addTablePrefix(tablePrefix) .addInclude(getTablesByModule(moduleName)); })4.2 Lombok与Swagger集成.entityBuilder() .enableLombok() .enableTableFieldAnnotation() .addSuperClass(BaseEntity.class) .versionColumnName(version) .logicDeleteColumnName(deleted); .controllerBuilder() .enableHyphenStyle() .enableRestStyle() .formatFileName(%sController);4.3 自定义模板覆盖在resources/templates目录下放置自定义模板templates/ ├── entity.java.ftl # 自定义实体模板 ├── mapper.java.ftl # 自定义Mapper接口模板 └── serviceImpl.java.ftl # 自定义Service实现模板配置模板引擎.templateEngine(new VelocityTemplateEngine() { Override public void init(ConfigBuilder configBuilder) { // 指定自定义模板位置 configBuilder.getTemplateConfig().setCustomTemplatePaths( Collections.singletonList(classpath:/templates/) ); } })5. 常见问题解决方案问题1生成的代码存在循环依赖现象OrderService引用了OrderMapper而OrderMapper又需要Order实体类解决使用Lazy注解或重构包结构问题2多模块间的类型引用现象API模块需要引用Domain模块的实体类解决配置生成器时将DTO生成到API模块.injectionConfig(builder - { builder.customMap(Collections.singletonMap( dtoPackage, com.example.order.api.dto )); })问题3生成代码不符合团队规范现象生成的代码风格与团队规范不一致解决定制模板文件或使用后置处理器.entityBuilder() .formatFileName(%sEntity) .addSuperClass(BaseEntity.class) .addIgnoreColumns(create_time, update_time);在最近的一个金融项目中我们通过定制模板实现了以下规范所有实体类继承BaseEntityService接口添加Transactional注解Controller统一返回Result包装类6. 性能优化建议当需要生成大量表时可以考虑以下优化手段并行生成将不同模块的生成任务并行化ListString modules Arrays.asList(order, payment, user); modules.parallelStream().forEach(MultiDataSourceGenerator::generate);增量生成记录已生成表的MD5签名避免重复生成.fileOverride(fileExists - { if (fileExists) { return checkFileChanged(fileExists); } return true; })缓存模板避免每次生成都重新编译Velocity模板.templateEngine(new CachedVelocityTemplateEngine())经过这些优化在一个包含200多张表的项目中生成时间从原来的8分钟缩短到2分钟以内。