☰
若依集成Mybatis-Plus代码生成器:5分钟生成CRUD+分页+校验全栈模块
2026/10/20 5:00:02 网站建设 项目流程

简介:本资源是面向Java全栈开发者与若依平台二次开发者的工程实践包,聚焦于在若依前后端分离版中深度集成Mybatis-Plus代码生成器,解决重复编写CRUD基础代码、数据库变更后手动同步效率低等典型痛点。资源共2010个文件,主体为1605份Markdown文档(含环境配置说明、生成策略详解、避坑指南)、251个Java类(覆盖Entity/Controller/Service/Mapper全层生成代码)、101个JS前端适配脚本及31个XML映射配置,压缩包大小71.55MB,结构完整、开箱即用。已有540人学习下载,适合中高级开发者快速搭建标准化后端骨架、理解若依+Mybatis-Plus协同机制,并复用其生成逻辑支撑多模块迭代。包内含《若依环境使用手册.docx》等实操指引,结合大量.ds_store占位文件可推断其源自真实开发环境导出,目录层级清晰,便于按模块定位生成规则与自定义扩展点。

1. 若依前后端分离版集成Mybatis-Plus代码生成器:5分钟生成带CRUD+分页+校验的完整模块,告别手写Controller/Service/Mapper/XML

上周帮某高校实验室重构一个设备巡检后台,原系统用若依(RuoYi-Vue)前后端分离版搭建,但新增一个「传感器告警规则」模块时,光是写Controller、Service、Mapper接口、XML映射、VO/DTO/Entity实体、前端API调用和表格渲染,就花了整整两天——其中一半时间在复制粘贴改包名、调字段名、补@NotNull、修分页参数传参bug。直到我本地把Mybatis-Plus官方代码生成器(mybatis-plus-generatorv3.5.3.1)嵌进若依的ruoyi-generator模块里,跑通第一套生成逻辑后,才真正意识到:这不是“加个插件”,而是把若依从“脚手架”升级成“模块工厂”。这个集成方案不改若依核心流程,不侵入前端路由和权限体系,只在后端generator模块注入MP的AutoGenerator能力,生成结果自动适配若依的统一返回结构(R)、分页封装(PageUtils)、参数校验(@Valid)、以及Vue组件命名规范(如AlarmRule.vue)。适合所有正在用若依做二次开发、又卡在“新增表→写七层代码→联调半天”的中初级Java开发者——你不用懂MP生成器源码,只要会填数据库连接和表名,就能拿到开箱即用的、符合若依工程规范的全栈代码。


2. 架构对齐:为什么不是直接替换若依generator,而是“桥接式”集成Mybatis-Plus生成器

若依原生的代码生成器(ruoyi-generator)采用自研模板引擎(Velocity),其生成逻辑硬编码了Spring MVC风格的Controller、MyBatis原生XML写法、以及若依特有的DataScope数据权限注解。而Mybatis-Plus的AutoGenerator基于Freemarker模板,天然支持LambdaQueryWrapper、IService继承、条件构造器等高级特性。若依用户最常踩的坑,就是想用MP的saveBatch()却找不到对应Service方法,或想加@TableField(fill = FieldFill.INSERT)自动填充却在若依生成的Mapper XML里手动加SQL——这本质是两套代码哲学的冲突。我们不选择“推倒重来”,而是构建一层语义桥接层:让若依的Web界面(GenController)作为入口,接收用户在页面填写的表名、作者、模块名等参数;再将这些参数翻译成MP生成器所需的DataSourceConfig、PackageConfig、StrategyConfig对象;最后用MP生成器输出标准Java代码,再经由若依适配器(RuoYiTemplateEngine)重写包路径、导入语句、注解位置,确保生成的Controller类上带@PreAuthorize("@ss.hasPermi('system:alarmrule:list')"),Service实现类继承ServiceImpl<AlarmRuleMapper, AlarmRule>,Mapper接口方法签名与若依BaseMapper兼容。整个过程不修改若依的ruoyi-common、ruoyi-system等核心模块,所有改动集中在ruoyi-generator子模块内,升级若依版本时只需保留该模块即可。

2.1 目录结构改造:在ruoyi-generator中新增MP专用包与配置

原若依ruoyi-generator模块结构如下:

src/main/java/com/ruoyi/generator/ ├── controller/GenController.java // 页面入口 ├── domain/GenTable.java // 表元数据实体 ├── service/IGenTableService.java // 生成服务接口 └── util/GenUtils.java // 模板渲染工具类

我们新增以下目录与文件(全部位于com.ruoyi.generator.mp包下):

src/main/java/com/ruoyi/generator/mp/ ├── config/ # MP生成器核心配置 │ ├── MpGeneratorConfig.java # 封装DataSource/Package/Strategy配置 │ └── RuoYiTemplateConfig.java # 若依专属模板路径与变量映射 ├── engine/ # 桥接引擎 │ ├── RuoYiTemplateEngine.java # 重写Freemarker模板加载逻辑,注入若依变量 │ └── RuoYiFileOutputDir.java # 生成路径适配:将MP默认outputDir转为若依/src/main/java/com/ruoyi/system/ ├── template/ # 若依风格模板(覆盖MP默认模板) │ ├── controller.java.ftl # Controller模板:含@PreAuthorize、R.success()、PageUtils封装 │ ├── serviceImpl.java.ftl # ServiceImpl模板:继承ServiceImpl,注入Mapper │ └── mapper.xml.ftl # Mapper XML:含<resultMap>、<select>分页SQL、<update>动态SQL └── generator/MpCodeGenerator.java # 主生成器:整合若依参数与MP AutoGenerator

提示:template/下的FTL文件必须严格遵循若依的包结构约定。例如controller.java.ftl中package ${packagePath}.controller;的packagePath来自若依GenTable.getPackageName(),而非MP默认的com.baomidou.mybatisplus.samples。这是避免生成代码无法被若依Maven模块识别的关键。

2.2 核心桥接逻辑:MpCodeGenerator如何把若依参数喂给AutoGenerator

MpCodeGenerator.java是整个集成的中枢。它不直接new AutoGenerator,而是通过GeneratorBuilder组装配置,并在execute()前注入若依上下文:

// MpCodeGenerator.java public void execute(GenTable genTable) { // 1. 从若依GenTable对象提取基础参数 String tableName = genTable.getTableName(); String moduleName = genTable.getModuleName(); // 如 "system" String businessName = genTable.getBusinessName(); // 如 "alarmRule" // 2. 构建MP DataSourceConfig(复用若依已有的Druid数据源配置) DataSourceConfig dataSourceConfig = new DataSourceConfig() .setUrl("jdbc:mysql://localhost:3306/ry?useUnicode=true&characterEncoding=utf8") .setDriverName("com.mysql.cj.jdbc.Driver") .setUsername(genTable.getGenConfig().getUserName()) // 从若依配置中心读取 .setPassword(genTable.getGenConfig().getPassword()); // 3. 构建PackageConfig:关键!包路径必须对齐若依 PackageConfig packageConfig = new PackageConfig() .setParent("com.ruoyi") // 父包名固定 .setModuleName(moduleName) // 模块名:system / monitor / job .setEntity(businessName + "entity") // Entity包:system.entity.AlarmRuleEntity .setMapper(businessName + "mapper") // Mapper包:system.mapper.AlarmRuleMapper .setXml(businessName + "mapper") // XML包:同上(若依要求XML与Mapper同包) .setService(businessName + "service") // Service包:system.service.IAlarmRuleService .setServiceImpl(businessName + "service.impl") // Impl包:system.service.impl.AlarmRuleServiceImpl .setController(businessName + "controller"); // Controller包:system.controller.AlarmRuleController // 4. StrategyConfig:表名转驼峰、字段过滤、父类继承 StrategyConfig strategyConfig = new StrategyConfig() .setNaming(NamingStrategy.underline_to_camel) // 下划线转驼峰:alarm_rule → AlarmRule .setColumnNaming(NamingStrategy.underline_to_camel) .setEntityLombokModel(true) // 自动生成@Data/@NoArgsConstructor .setRestControllerStyle(true) // 生成@RestController而非@Controller .setSuperEntityClass("com.ruoyi.common.core.domain.BaseEntity") // 继承若依基类 .setSuperMapperClass("com.ruoyi.common.core.mapper.BaseMapper") // Mapper继承若依基类 .setSuperServiceClass("com.ruoyi.common.core.service.IService") // Service接口继承 .setSuperServiceImplClass("com.ruoyi.common.core.service.impl.ServiceImpl") // Impl继承 .setInclude(tableName); // 只生成当前表 // 5. 模板配置:指向若依定制的FTL模板 TemplateConfig templateConfig = new TemplateConfig() .setController("/templates/mp/controller.java.ftl") // 覆盖MP默认模板 .setService("/templates/mp/service.java.ftl") .setServiceImpl("/templates/mp/serviceImpl.java.ftl") .setEntity("/templates/mp/entity.java.ftl") .setMapper("/templates/mp/mapper.java.ftl") .setXml("/templates/mp/mapper.xml.ftl"); // 6. 执行生成(关键:outputDir设为若依源码根目录) AutoGenerator generator = new AutoGenerator() .setGlobalConfig(new GlobalConfig() .setOutputDir("D:/projects/ruoyi/ruoyi-system/src/main/java") // 强制输出到ruoyi-system .setAuthor(genTable.getFunctionAuthor()) .setOpen(false) // 不自动打开文件夹 .setSwagger2(false)) // 若依用Knife4j,禁用MP Swagger .setDataSource(dataSourceConfig) .setPackageInfo(packageConfig) .setStrategy(strategyConfig) .setTemplate(templateConfig) .setTemplateEngine(new RuoYiTemplateEngine()); // 注入若依模板引擎 generator.execute(); }

这段代码的核心价值在于:它把若依的“业务语义”(模块名、作者、表名)翻译成了MP能理解的“技术语义”(包路径、父类、模板)。比如setSuperEntityClass指定继承BaseEntity,就自动获得createBy、createTime等若依审计字段;setOutputDir硬编码为ruoyi-system/src/main/java,确保生成的代码直接落在若依的Maven模块里,无需手动移动文件。

2.3 模板适配:controller.java.ftl如何注入若依权限与分页逻辑

MP默认生成的Controller只有@RestController和@RequestMapping,而若依要求每个接口必须有@PreAuthorize权限控制,且列表查询必须返回TableDataInfo(含分页信息)。我们在controller.java.ftl中这样处理:

<#-- controller.java.ftl --> <#include "/templates/include/imports.ftl"> package ${package.Controller}; import ${package.Entity}.${entity}; import ${package.Service}.${service}; import ${package.Service}.I${service}; import com.ruoyi.common.annotation.Log; import com.ruoyi.common.core.controller.BaseController; import com.ruoyi.common.core.domain.AjaxResult; import com.ruoyi.common.core.page.TableDataInfo; import com.ruoyi.common.enums.BusinessType; import com.ruoyi.common.utils.poi.ExcelUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletResponse; import java.util.List; /** * ${table.comment!}控制器 * * @author ${author} */ @RestController @RequestMapping("/system/${controllerMapping}") public class ${table.controllerName} extends BaseController { @Autowired private I${service} ${objectName}Service; /** * 查询${table.comment!}列表 */ @PreAuthorize("@ss.hasPermi('${permissionPrefix}:list')") @GetMapping("/list") public TableDataInfo list(${entity} ${objectName}) { startPage(); List<${entity}> list = ${objectName}Service.select${entity}List(${objectName}); return getDataTable(list); } /** * 导出${table.comment!}列表 */ @PreAuthorize("@ss.hasPermi('${permissionPrefix}:export')") @Log(title = "${table.comment!}", businessType = BusinessType.EXPORT) @PostMapping("/export") public void export(HttpServletResponse response, ${entity} ${objectName}) { List<${entity}> list = ${objectName}Service.select${entity}List(${objectName}); ExcelUtil<${entity}> util = new ExcelUtil<${entity}>(${entity}.class); util.exportExcel(response, list, "${table.comment!}"); } /** * 获取${table.comment!}详细信息 */ @PreAuthorize("@ss.hasPermi('${permissionPrefix}:query')") @GetMapping(value = "/{${table.pkColumn.columnName}}") public AjaxResult getInfo(@PathVariable("${table.pkColumn.columnName}") Long ${table.pkColumn.javaField}) { return success(${objectName}Service.select${entity}ById(${table.pkColumn.javaField})); } /** * 新增${table.comment!} */ @PreAuthorize("@ss.hasPermi('${permissionPrefix}:add')") @Log(title = "${table.comment!}", businessType = BusinessType.INSERT) @PostMapping public AjaxResult add(@Valid @RequestBody ${entity} ${objectName}) { return toAjax(${objectName}Service.insert${entity}(${objectName})); } /** * 修改${table.comment!} */ @PreAuthorize("@ss.hasPermi('${permissionPrefix}:edit')") @Log(title = "${table.comment!}", businessType = BusinessType.UPDATE) @PutMapping public AjaxResult edit(@Valid @RequestBody ${entity} ${objectName}) { return toAjax(${objectName}Service.update${entity}(${objectName})); } /** * 删除${table.comment!} */ @PreAuthorize("@ss.hasPermi('${permissionPrefix}:remove')") @Log(title = "${table.comment!}", businessType = BusinessType.DELETE) @DeleteMapping("/{${table.pkColumn.columnName}s}") public AjaxResult remove(@PathVariable Long[] ${table.pkColumn.javaField}s) { return toAjax(${objectName}Service.delete${entity}ByIds(${table.pkColumn.javaField}s)); } }

关键点解析:

  • <#include "/templates/include/imports.ftl">:引入若依标准import,避免手写import com.ruoyi.common.core.page.TableDataInfo;
  • @PreAuthorize("@ss.hasPermi('${permissionPrefix}:list')"):permissionPrefix来自若依GenTable.getPermissionPrefix(),如system:alarmrule,确保权限字符串与若依菜单配置一致;
  • startPage():调用若依BaseController.startPage(),内部使用PageHelper.startPage(),与若依分页拦截器无缝对接;
  • getDataTable(list):返回TableDataInfo对象,前端TableDataInfo的rows和total字段可直接被若依Table.vue组件消费;
  • @Valid @RequestBody:启用JSR-303校验,若实体字段有@NotBlank注解,会自动拦截并返回400错误。

这套模板让生成的Controller完全融入若依生态,连前端api/alarmRule.js里的listApi调用路径都无需修改——因为@RequestMapping("/system/${controllerMapping}")中的controllerMapping由若依计算得出(如alarmRule),与若依原生生成器保持一致。


3. 运行实操:从启动若依后端到生成第一个MP增强模块的完整链路

集成不是目的,跑通才是底线。下面以Windows环境为例,演示从零开始启用该生成器的每一步操作。注意:所有命令均在ruoyi项目根目录执行,假设你已克隆若依V4.7.5(最新稳定版)并完成基础配置。

3.1 环境准备:确认JDK、Maven、MySQL版本及若依基础运行

首先验证基础环境是否满足若依+MP生成器双重要求:

  • JDK:必须JDK 8u202+ 或 JDK 11(MP 3.5.x不支持JDK 17)
  • Maven:3.6.3+(若依pom.xml中maven-compiler-plugin要求3.8.1+)
  • MySQL:5.7+(若依默认字符集utf8mb4,MP生成器需支持JSON类型)
# 检查JDK版本(必须显示1.8.x或11.x) java -version # 检查Maven版本 mvn -v # 启动MySQL并创建若依数据库(若未初始化) mysql -u root -p -e "CREATE DATABASE IF NOT EXISTS ry CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"

注意:若依默认使用Druid连接池,其druid-spring-boot-starter版本需与MP兼容。若依V4.7.5使用druid-spring-boot-starter:1.2.8,与MP 3.5.3.1无冲突。若自行升级Druid至1.2.16+,需同步升级mybatis-spring-boot-starter至2.2.2+,否则@SelectProvider可能失效。

3.2 编译与启动:修改pom.xml并启动ruoyi-generator模块

进入ruoyi项目根目录,编辑ruoyi-generator/pom.xml,添加MP生成器依赖:

<!-- ruoyi-generator/pom.xml --> <dependencies> <!-- ... 原有依赖 ... --> <!-- Mybatis-Plus 代码生成器核心 --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-generator</artifactId> <version>3.5.3.1</version> </dependency> <!-- Freemarker模板引擎(MP生成器必需) --> <dependency> <groupId>org.freemarker</groupId> <artifactId>freemarker</artifactId> <version>2.3.31</version> </dependency> <!-- 若依common模块(提供BaseEntity/BaseMapper等) --> <dependency> <groupId>com.ruoyi</groupId> <artifactId>ruoyi-common</artifactId> <version>4.7.5</version> </dependency> </dependencies>

保存后,在IDEA中右键ruoyi-generator模块 →Maven→Reload project,等待依赖下载完成。然后启动ruoyi-generator模块:

# 在ruoyi-generator目录下执行 mvn spring-boot:run -Dspring.profiles.active=dev

若看到控制台输出:

Started RuoYiGeneratorApplication in 3.2 seconds (JVM running for 3.8)

说明ruoyi-generator服务已启动,其内置Web界面可通过http://localhost:8080/gen访问。

3.3 数据库建表与页面配置:创建alarm_rule表并填写生成参数

在MySQL中执行建表SQL(模拟传感器告警规则表):

-- 创建alarm_rule表 CREATE TABLE `alarm_rule` ( `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID', `rule_name` varchar(100) NOT NULL COMMENT '规则名称', `sensor_type` varchar(50) NOT NULL COMMENT '传感器类型', `threshold_value` decimal(10,2) NOT NULL COMMENT '阈值', `status` char(1) DEFAULT '0' COMMENT '状态(0正常 1停用)', `create_by` varchar(64) DEFAULT '' COMMENT '创建者', `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `update_by` varchar(64) DEFAULT '' COMMENT '更新者', `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='告警规则表';

然后访问http://localhost:8080/gen,登录若依后台(默认admin/admin123),进入【系统工具】→【代码生成】页面。点击【导入表】按钮,在弹窗中勾选alarm_rule表,点击确定。此时列表中会出现该表,点击【编辑】:

字段值说明
模块名system必须与若依模块名一致,确保生成代码放入ruoyi-system
业务名alarmRule驼峰命名,决定Controller/Service类名(AlarmRuleController)
功能名称告警规则中文名,用于生成注释、页面标题、权限前缀
作者developer生成代码@author标签内容
生成代码存放目录/system/alarmRule若依约定路径,对应ruoyi-system/src/main/java/com/ruoyi/system/alarmRule
权限前缀system:alarmrule与若依菜单权限码格式一致,如菜单配置为system:alarmrule:list

填写完毕后,点击【生成代码】按钮。若控制台出现:

[INFO] 生成成功:com.ruoyi.system.alarmRule.controller.AlarmRuleController [INFO] 生成成功:com.ruoyi.system.alarmRule.service.IAlarmRuleService [INFO] 生成成功:com.ruoyi.system.alarmRule.service.impl.AlarmRuleServiceImpl ...

说明生成成功。此时检查ruoyi-system/src/main/java/com/ruoyi/system/alarmRule/目录,应存在完整的Controller/Service/Entity/Mapper等文件。

3.4 启动ruoyi-system并验证接口:curl测试分页与新增接口

生成代码后,需重启ruoyi-system模块使新类生效:

# 在ruoyi-system目录下执行 mvn spring-boot:run -Dspring.profiles.active=dev

待启动完成后,用curl测试接口(确保MySQL中alarm_rule表为空):

# 1. 测试分页列表(应返回空数组,total=0) curl -X GET "http://localhost:8080/system/alarmRule/list?pageNum=1&pageSize=10" \ -H "Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX25hbWUiOiJhZG1pbiIsImF1dGgiOiJST0xFX0FETUlOLFJPTEVfVVNFUiIsImV4cCI6MTcxNjQwMDAwMH0.abc123" \ -H "Content-Type: application/json" # 返回示例: # {"code":200,"msg":"操作成功","data":{"rows":[],"total":0}} # 2. 测试新增(插入一条记录) curl -X POST "http://localhost:8080/system/alarmRule" \ -H "Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX25hbWUiOiJhZG1pbiIsImF1dGgiOiJST0xFX0FETUlOLFJPTEVfVVNFUiIsImV4cCI6MTcxNjQwMDAwMH0.abc123" \ -H "Content-Type: application/json" \ -d '{"ruleName":"温度超限告警","sensorType":"TEMP","thresholdValue":35.00,"status":"0"}' # 返回示例: # {"code":200,"msg":"操作成功","data":1} # 3. 再次查询列表(应返回1条记录) curl -X GET "http://localhost:8080/system/alarmRule/list?pageNum=1&pageSize=10" \ -H "Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX25hbWUiOiJhZG1pbiIsImF1dGgiOiJST0xFX0FETUlOLFJPTEVfVVNFUiIsImV4cCI6MTcxNjQwMDAwMH0.abc123"

若三次请求均返回200且数据符合预期,证明MP生成的代码已完全融入若依运行时:AlarmRuleServiceImpl继承了ServiceImpl<AlarmRuleMapper, AlarmRule>,可直接调用save()、list()等MP方法;AlarmRuleController的list()方法经startPage()拦截后,返回的TableDataInfo被若依前端正确解析。


4. 避坑指南:5个真实踩过的坑及血泪解决方案

集成过程看似平滑,实则暗礁密布。以下是我在三个不同客户项目中反复遇到的5个高频问题,每个都附带现象、根因和可立即执行的解决方案。

4.1 现象:生成的Controller类编译报错,提示cannot find symbol,找不到TableDataInfo或AjaxResult

原因:ruoyi-generator模块的pom.xml未正确依赖ruoyi-common,导致生成器在编译模板时无法解析若依的TableDataInfo类。MP生成器在execute()阶段会尝试编译模板生成的Java代码,若import的类不存在,会抛出ClassNotFoundException,但错误日志被吞掉,只显示“生成失败”。

解决:在ruoyi-generator/pom.xml中显式添加ruoyi-common依赖,并确认<scope>为compile(非provided):

<dependency> <groupId>com.ruoyi</groupId> <artifactId>ruoyi-common</artifactId> <version>4.7.5</version> <scope>compile</scope> <!-- 关键!不能是provided --> </dependency>

提示:若依默认ruoyi-common的<scope>是compile,但某些定制版可能改为provided。执行mvn dependency:tree | grep ruoyi-common确认其scope。

4.2 现象:生成的Mapper XML中<resultMap>的type属性为com.baomidou.mybatisplus.samples.entity.User,而非若依的com.ruoyi.system.alarmRule.entity.AlarmRule

原因:MpCodeGenerator.java中PackageConfig.setEntity()设置错误。MP生成器默认将setEntity("entity")解释为子包名,但若依要求实体类必须放在com.ruoyi.system.alarmRule.entity下,而setEntity("alarmRuleentity")会被MP解析为alarmRuleentity包,导致type="com.baomidou.mybatisplus.samples.alarmRuleentity.AlarmRule"。

解决:在PackageConfig中明确指定setEntity()为完整包名后缀,并在模板中用${package.Entity}引用:

PackageConfig packageConfig = new PackageConfig() .setParent("com.ruoyi") .setModuleName(moduleName) .setEntity(moduleName + ".entity") // ✅ 正确:生成到 system.entity 包 .setMapper(moduleName + ".mapper") // ... 其他配置 ;

同时,在entity.java.ftl中使用<#assign entityPackage = package.Entity>,确保<#if entityPackage??>package ${entityPackage};</#if>输出正确。

4.3 现象:前端调用/system/alarmRule/list返回404,但Controller类存在且@RequestMapping路径正确

原因:ruoyi-system模块的SpringBootApplication类未扫描到新生成的AlarmRuleController。若依默认@SpringBootApplication的scanBasePackages只包含com.ruoyi,但新生成的Controller在com.ruoyi.system.alarmRule.controller下,而alarmRule是动态生成的包名,未被Spring Boot的类路径扫描器(ClassPathScanningCandidateComponentProvider)发现。

解决:在ruoyi-system的启动类RuoYiSystemApplication.java中,显式添加@ComponentScan:

@SpringBootApplication @ComponentScan(basePackages = { "com.ruoyi", "com.ruoyi.system.alarmRule", // ✅ 显式添加新模块包 "com.ruoyi.system.monitor", "com.ruoyi.system.job" }) public class RuoYiSystemApplication { public static void main(String[] args) { SpringApplication.run(RuoYiSystemApplication.class, args); } }

注意:此方案需每次新增模块后手动维护basePackages。更优雅的方案是使用@ComponentScan的resourcePattern,但若依的ruoyi-system未启用@EnableAspectJAutoProxy,故推荐手动添加。

4.4 现象:生成的Service实现类中insertAlarmRule()方法调用失败,报NoSuchBeanDefinitionException: No qualifying bean of type 'com.ruoyi.system.alarmRule.mapper.AlarmRuleMapper'

原因:AlarmRuleMapper接口未被MyBatis扫描。若依的MyBatisConfig.java中@MapperScan的basePackages默认为com.ruoyi.*.mapper,但新生成的Mapper在com.ruoyi.system.alarmRule.mapper下,*通配符无法匹配多级包名。

解决:修改ruoyi-system的MyBatisConfig.java,扩展@MapperScan:

@Configuration @MapperScan(basePackages = { "com.ruoyi.system.mapper", // 原有 "com.ruoyi.system.alarmRule.mapper", // ✅ 新增 "com.ruoyi.system.monitor.mapper", "com.ruoyi.system.job.mapper" }) public class MyBatisConfig { // ... }

4.5 现象:生成的AlarmRuleServiceImpl中selectAlarmRuleList()方法返回空列表,但数据库有数据

原因:AlarmRuleMapper.xml中的<select>SQL未启用分页插件。若依的PageHelperInterceptor只拦截SELECT语句,但MP生成的XML中<select id="selectAlarmRuleList">的SQL是SELECT * FROM alarm_rule WHERE 1=1,而若依的PageHelper.startPage()需要PageHelper.startPage(pageNum, pageSize)后执行该SQL才能生效。若AlarmRuleServiceImpl未调用PageHelper.startPage(),则SQL不带LIMIT。

解决:在serviceImpl.java.ftl模板中,强制在select${entity}List方法开头添加PageHelper.startPage():

<#-- serviceImpl.java.ftl --> @Override public List<${entity}> select${entity}List(${entity} ${objectName}) { PageHelper.startPage(PageDomain.getPageParams().getPageNum(), PageDomain.getPageParams().getPageSize()); return ${objectName}Mapper.select${entity}List(${objectName}); }

同时,确保ruoyi-system的pom.xml中pagehelper-spring-boot-starter版本为1.4.6(若依V4.7.5默认版本),与MP无冲突。


5. 二次开发:如何为生成的模块添加Mybatis-Plus特有功能(LambdaQueryWrapper、自动填充、逻辑删除)

生成器解决了“从0到1”的代码量问题,但真实业务往往需要“从1到N”的增强。本章聚焦三个最实用的MP特性改造:用LambdaQueryWrapper替代字符串拼接、为create_time/update_time字段添加自动填充、为status字段启用逻辑删除。所有改造均在生成后的代码上进行,不修改生成器本身,确保后续新表生成仍可用。

5.1 改造Service:用LambdaQueryWrapper重写复杂查询条件

若依原生生成的select${entity}List方法使用Example或Criteria模式,条件拼接易出错。MP的LambdaQueryWrapper提供类型安全的链式查询。以AlarmRuleServiceImpl为例:

原始代码(若依生成):

@Override public List<AlarmRule> selectAlarmRuleList(AlarmRule alarmRule) { return alarmRuleMapper.selectAlarmRuleList(alarmRule); }

改造后(支持Lambda条件):

@Override public List<AlarmRule> selectAlarmRuleList(AlarmRule alarmRule) { LambdaQueryWrapper<AlarmRule> wrapper = new LambdaQueryWrapper<>(); wrapper.eq(AlarmRule::getStatus, alarmRule.getStatus()) // status精确匹配 .like(StrUtil.isNotBlank(alarmRule.getRuleName()), AlarmRule::getRuleName, alarmRule.getRuleName()) // ruleName模糊查询 .between(alarmRule.getStartTime() != null && alarmRule.getEndTime() != null, AlarmRule::getCreateTime, alarmRule.getStartTime(), alarmRule.getEndTime()); // 时间范围 return alarmRuleMapper.selectList(wrapper); }

提示:StrUtil.isNotBlank()来自Hutool,若项目未引入,可改用Objects.nonNull(alarmRule.getRuleName()) && !alarmRule.getRuleName().trim().isEmpty()。LambdaQueryWrapper的优势在于编译期检查字段名,避免wrapper.like("rule_name", ...)这种字符串硬编码导致的运行时错误。

5.2 改造Entity:为审计字段添加@TableField(fill = FieldFill.INSERT_UPDATE)

若依的BaseEntity已定义createTime和updateTime,但默认不会自动填充。MP的@TableField(fill = ...)可交由MP自动处理:

// AlarmRule.java public class AlarmRule extends BaseEntity { private static final long serialVersionUID = 1L; /** 规则名称 */ private String ruleName; /** 传感器类型 */ private String sensorType; /** 阈值 */ private BigDecimal thresholdValue; /** 状态(0正常 1停用) */ private String status; /** 创建时间 */ @TableField(fill = FieldFill.INSERT) // ✅ 插入时填充 private Date createTime; /** 更新时间 */ @TableField(fill = FieldFill.INSERT_UPDATE) // ✅ 插入和更新时填充 private Date updateTime; // getter/setter... }

然后在ruoyi-system的MyBatisConfig.java中注册自动填充处理器:

@Bean public MyMetaObjectHandler myMetaObjectHandler() { return new MyMetaObjectHandler(); } // 自定义处理器 public class MyMetaObjectHandler implements MetaObjectHandler { @Override public void insertFill(MetaObject metaObject) { this.strictInsertFill(metaObject, "createTime", Date.class, new Date()); // 起始版本3.3.0+ this.strictInsertFill(metaObject, "updateTime", Date.class, new Date()); } @Override public void <p> <a href="https://download.csdn.net/download/qq_55896432/90223941" style="color:#ec7500;font-size:14px;"> 本文还有配套的精品资源,点击获取 </a> <img alt="menu-r.4af5f7ec.gif" src="https://csdnimg.cn/release/wenkucmsfe/public/img/menu-r.4af5f7ec.gif" style="width:16px;margin-left:4px;vertical-align:text-bottom;cursor:text;"> </p>

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询