☰
若依前后端分离版集成Mybatis-Plus代码生成器实战指南
2026/10/20 13:59:18 网站建设 项目流程

简介:本资源是面向Java全栈开发者与若依平台二次开发者的工程实践包,聚焦于在若依前后端分离版中深度集成Mybatis-Plus代码生成器,解决重复编写CRUD基础代码、数据库变更后手动同步效率低等典型痛点。资源共2010个文件,主体为1605份Markdown文档(含环境配置指南、生成器使用手册、常见问题排错说明)、251个Java类(覆盖Entity、Mapper、Service及Controller模板)、101个JS前端适配脚本,辅以31个XML配置与2个SQL建表脚本,整体71.55MB,结构完整、开箱即用。已有540人学习下载,适合中高级开发者快速搭建标准化后端骨架,并支持后续迭代中一键重生成。读者可直接复用其生成策略配置、多模块目录组织方式及若依+Mybatis-Plus协同规范,显著缩短项目初始化周期,降低维护成本。

1. 若依前后端分离版集成Mybatis-Plus代码生成器:不是加个插件就完事,而是要让生成的代码真正跑进你的业务流水线里

很多同学在毕设或课设里选了若依(RuoYi)前后端分离版,想快速搭出一个带CRUD的管理后台——结果卡在「怎么把Mybatis-Plus的代码生成器塞进去」这一步。不是生成不了,是生成出来的Mapper、Service、Controller要么包路径错乱、要么字段映射失败、要么连不上数据库连接池;更常见的是:生成器能跑通,但生成的代码一放进若依工程里就编译报红、启动报NoSuchBeanDefinitionException、前端调用404。这不是工具不行,是没理清若依的模块分层逻辑和Mybatis-Plus生成器的注入时机。这套方案专为「已拉下若依Vue+SpringBoot标准分支、正卡在代码生成环节」的同学设计:它不讲原理,只给可粘贴、可调试、可答辩的完整集成路径——从pom依赖冲突解决、到生成模板定制、再到生成后自动注册进若依权限体系,每一步都对应真实翻车现场。适合做毕设/课设的同学直接套用结构,也适合刚接手若依二次开发的工程师快速落地。


2. 总体架构设计:三层解耦与四个关键注入点的职责划分

若依前后端分离版采用典型的分层架构:前端Vue独立部署,后端SpringBoot提供REST API,数据库通过Druid连接池管理。而Mybatis-Plus代码生成器(MPG)本质是一个运行时代码生成工具,它不参与运行期逻辑,但其输出必须无缝融入若依的已有结构中。因此,集成不是“把MPG加进pom然后执行main方法”,而是明确四个关键注入点,并确保每个点都与若依当前版本(v4.7.0+)的约定一致:

  • 注入点①:依赖坐标与版本对齐—— 若依默认使用Mybatis-Plus 3.4.3.4,而新版MPG 3.5.x起要求Java 17+且移除了AutoGenerator类,必须降级或适配;
  • 注入点②:模板引擎与包路径策略—— 若依的Controller放在com.ruoyi.web.controller.system,Service在com.ruoyi.service,但MPG默认生成在com.example下,需重写包名策略;
  • 注入点③:数据源与事务管理器绑定—— 若依使用DruidDataSource+DataSourceTransactionManager,MPG生成的Mapper XML需被SqlSessionFactory扫描,否则@MapperScan失效;
  • 注入点④:生成后自动注册进若依菜单/权限体系—— 若依所有功能模块需在sys_menu表中预置记录,否则生成的页面无法在左侧菜单显示,用户无权访问。

这四个点环环相扣:依赖错,生成器启动失败;包路径错,Spring找不到Bean;数据源未绑定,Mapper执行SQL时报空指针;菜单未注册,前端压根看不到新页面。下面逐个击破。

2.1 依赖配置:锁定Mybatis-Plus 3.4.3.4并排除冲突传递依赖

若依v4.7.0的pom.xml中已声明mybatis-plus-boot-starter:3.4.3.4,但MPG官方starter(mybatis-plus-generator)在3.5.0+版本中强制升级了mybatis-plus-core至3.5.x,导致LambdaQueryWrapper构造异常、@TableName注解失效。必须手动降级并切断传递依赖:

<!-- 在ruoyi-admin模块的pom.xml中,<dependencies>内添加 --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-generator</artifactId> <version>3.4.1</version> <exclusions> <exclusion> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-core</artifactId> </exclusion> <exclusion> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-extension</artifactId> </exclusion> </exclusions> </dependency>

提示:mybatis-plus-generator:3.4.1是最后一个兼容mybatis-plus-boot-starter:3.4.3.4的稳定版。不要用3.5.0及以上,否则StrategyConfig.setInclude()会抛NoSuchMethodError——这是学生答辩时最常被导师问住的点:为什么生成器跑不通?答“版本不匹配”比“不知道”强十倍。

同时,在ruoyi-common模块中确认mybatis-plus-boot-starter版本未被其他依赖覆盖:

<dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.4.3.4</version> </dependency>

验证方式:执行mvn dependency:tree | grep "mybatis-plus",确保输出中只有3.4.3.4和3.4.1,无3.5.x字样。

2.2 包路径与模块归属策略:按若依标准目录树生成代码

若依的模块划分非常严格:ruoyi-system负责系统管理(用户、角色、菜单),ruoyi-framework是基础框架,ruoyi-admin是启动模块。MPG默认生成代码到com.example,必须重写PackageConfig,使其严格遵循若依的包结构:

// 在ruoyi-admin/src/main/java/com/ruoyi/generator/CodeGenerator.java中 PackageConfig pc = new PackageConfig(); pc.setParent("com.ruoyi"); // 根包名 pc.setModuleName("system"); // 模块名 → 对应ruoyi-system模块 pc.setEntity("domain"); // 实体类放com.ruoyi.system.domain pc.setMapper("mapper"); // Mapper接口放com.ruoyi.system.mapper pc.setXml("mapper"); // XML放resources/mapper/system/ pc.setService("service"); // Service放com.ruoyi.system.service pc.setServiceImpl("service.impl"); // 实现类放com.ruoyi.system.service.impl pc.setController("web.controller.system"); // Controller放com.ruoyi.web.controller.system

关键细节:

  • moduleName("system")不是随便写的——它决定了生成文件的子目录层级,也影响后续菜单注册脚本的路径拼接;
  • setXml("mapper")配合GlobalConfig.setOutputDir()指向ruoyi-system/src/main/resources/mapper/system/,否则XML无法被SqlSessionFactory扫描;
  • Controller必须落在web.controller.system,因为若依的@RequestMapping("/system")统一前缀由该包下Controller继承,否则前端请求/system/user/list会404。

生成后,检查ruoyi-system模块下的实际目录结构是否如下(以user表为例):

ruoyi-system/ ├── src/main/java/com/ruoyi/system/domain/User.java ├── src/main/java/com/ruoyi/system/mapper/UserMapper.java ├── src/main/resources/mapper/system/UserMapper.xml ├── src/main/java/com/ruoyi/system/service/UserService.java ├── src/main/java/com/ruoyi/system/service/impl/UserServiceImpl.java └── src/main/java/com/ruoyi/web/controller/system/UserController.java

若目录错位(如Controller生成在com.ruoyi.system.controller),则必须修改pc.setController(),不能靠IDE手动挪动——因为挪动后@PreAuthorize("@ss.hasPermi('system:user:list')")权限注解会因包路径变化失效。

2.3 数据源与Mapper扫描绑定:让生成的Mapper被Spring真正识别

若依使用DruidDataSource并通过MyBatisConfig类配置SqlSessionFactory,其@MapperScan默认扫描com.ruoyi.*.mapper包。但MPG生成的Mapper接口若放在com.ruoyi.system.mapper下,仍可能因MapperScannerConfigurer初始化顺序问题导致Bean未注册。

解决方案:在ruoyi-system模块的resources/META-INF/spring.factories中显式声明Mapper扫描器(若不存在则新建):

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.ruoyi.system.config.MyBatisPlusConfig

并在ruoyi-system/src/main/java/com/ruoyi/system/config/MyBatisPlusConfig.java中定义:

@Configuration @MapperScan(basePackages = {"com.ruoyi.system.mapper"}, sqlSessionTemplateRef = "sqlSessionTemplate") public class MyBatisPlusConfig { @Bean @Primary public SqlSessionTemplate sqlSessionTemplate(@Qualifier("sqlSessionFactory") SqlSessionFactory sqlSessionFactory) { return new SqlSessionTemplate(sqlSessionFactory); } }

注意:@MapperScan的basePackages值必须与MPG生成的pc.setMapper("mapper")完全一致,且sqlSessionTemplateRef必须指向若依主配置中定义的sqlSessionFactoryBean名(查看ruoyi-framework/src/main/java/com/ruoyi/framework/config/MyBatisConfig.java确认)。

验证方式:启动项目后,在http://localhost:8080/druid/sql.html中执行任意一条SELECT * FROM sys_user LIMIT 1,观察Druid监控页是否出现该SQL——若出现,说明Mapper已成功注入;若不出现,检查MyBatisPlusConfig是否被Spring加载(在类上加@PostConstruct打印日志)。

2.4 菜单与权限自动注册:生成代码后一键写入sys_menu表

若依所有功能入口必须在sys_menu表中存在记录,否则即使Controller能响应请求,前端菜单也不显示,用户点击即404。MPG本身不处理此逻辑,需在生成完成后自动插入菜单数据。

我们在CodeGenerator.java末尾追加菜单注册逻辑:

// 生成完毕后,自动向sys_menu插入记录 private static void insertMenu(String tableName, String tableComment) { JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource()); String sql = "INSERT INTO sys_menu (menu_name, parent_id, order_num, url, menu_type, visible, perms, icon, create_by, create_time) " + "VALUES (?, 0, (SELECT IFNULL(MAX(order_num), 0) + 1 FROM sys_menu WHERE parent_id = 0), '/system/" + tableName + "', 'C', '0', 'system:" + tableName + ":view', 'fa fa-circle-o', 'admin', NOW())"; jdbcTemplate.update(sql, tableComment); // 如"用户管理" // 插入子菜单:查询、新增、修改、删除 Long parentId = jdbcTemplate.queryForObject("SELECT menu_id FROM sys_menu WHERE perms = 'system:" + tableName + ":view'", Long.class); String[] perms = {"list", "add", "edit", "remove"}; String[] names = {"查询", "新增", "修改", "删除"}; for (int i = 0; i < perms.length; i++) { jdbcTemplate.update( "INSERT INTO sys_menu (menu_name, parent_id, order_num, url, menu_type, visible, perms, icon, create_by, create_time) " + "VALUES (?, ?, ?, '#', 'F', '0', 'system:" + tableName + ":" + perms[i] + "', '', 'admin', NOW())", names[i], parentId, i + 1 ); } }

其中dataSource()方法返回若依的DruidDataSource实例(需通过ApplicationContext获取,此处为简化展示,实际需注入DataSourceBean)。

关键设计点:菜单perms字段格式必须为system:xxx:view(列表页)、system:xxx:list(查询按钮权限),与若依前端v-permission指令和后端@PreAuthorize注解严格对应。若写成user:list或sys_user:list,权限校验永远失败。

生成后,登录若依后台(账号admin/admin123),进入【系统管理】→【菜单管理】,搜索用户管理,确认主菜单及四个子菜单均已存在,且perms列值与代码中拼接的一致。


3. 核心模块实现:从数据库表到可运行页面的六步闭环

集成不是终点,而是让生成的代码真正跑起来。以下以sys_user表为例,走通从建表→生成→编译→启动→菜单→权限的完整闭环。每一步都对应毕设答辩中“请演示一下你做的功能”的实操环节。

3.1 数据库准备:按若依规范建表并填充基础数据

若依要求所有业务表必须有create_by、create_time、update_by、update_time、remark等公共字段。sys_user表结构需严格遵循:

CREATE TABLE `sys_user` ( `user_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '用户ID', `dept_id` bigint(20) DEFAULT NULL COMMENT '部门ID', `login_name` varchar(30) NOT NULL COMMENT '登录账号', `user_name` varchar(30) NOT NULL COMMENT '用户昵称', `user_type` varchar(2) DEFAULT '00' COMMENT '用户类型(00系统用户)', `email` varchar(50) DEFAULT '' COMMENT '用户邮箱', `phonenumber` varchar(11) DEFAULT '' COMMENT '手机号码', `sex` char(1) DEFAULT '0' COMMENT '用户性别(0男 1女 2未知)', `avatar` varchar(100) DEFAULT '' COMMENT '头像路径', `password` varchar(50) NOT NULL COMMENT '密码', `salt` varchar(20) DEFAULT '' COMMENT '盐加密', `status` char(1) DEFAULT '0' COMMENT '帐号状态(0正常 1停用)', `del_flag` char(1) DEFAULT '0' COMMENT '删除标志(0代表存在 2代表删除)', `login_ip` varchar(128) DEFAULT '' COMMENT '最后登录IP', `login_date` datetime DEFAULT NULL COMMENT '最后登录时间', `create_by` varchar(64) DEFAULT '' COMMENT '创建者', `create_time` datetime DEFAULT NULL COMMENT '创建时间', `update_by` varchar(64) DEFAULT '' COMMENT '更新者', `update_time` datetime DEFAULT NULL COMMENT '更新时间', `remark` varchar(500) DEFAULT NULL COMMENT '备注', PRIMARY KEY (`user_id`) ) ENGINE=InnoDB AUTO_INCREMENT=100 DEFAULT CHARSET=utf8mb4 COMMENT='用户信息表';

注意:del_flag字段值必须为'0'(字符型),若设为0(数字型),Mybatis-Plus的LogicDelete逻辑删除会失效;status同理。这是学生最容易忽略的“玄学”坑——表建对了,但删除按钮点了没反应,查半天发现是字段类型错了。

插入一条测试数据:

INSERT INTO `sys_user` (`user_id`, `login_name`, `user_name`, `email`, `phonenumber`, `password`, `salt`, `status`, `del_flag`, `create_by`, `create_time`) VALUES (101, 'testuser', '测试用户', 'test@example.com', '13800138000', 'e10adc3949ba59abbe56e057f20f883e', '123456', '0', '0', 'admin', NOW());

3.2 执行代码生成:定制化模板与字段过滤

MPG默认生成所有字段,但若依的sys_user表中password、salt等敏感字段不应出现在列表页和表单中。需在StrategyConfig中配置字段过滤:

StrategyConfig strategy = new StrategyConfig(); strategy.setNaming(NamingStrategy.underline_to_camel); // 下划线转驼峰 strategy.setColumnNaming(NamingStrategy.underline_to_camel); strategy.setEntityLombokModel(true); // 使用Lombok strategy.setRestControllerStyle(true); // 生成@RestController strategy.setInclude("sys_user"); // 只生成指定表 strategy.setTablePrefix("sys_"); // 表前缀,生成类名去掉前缀 → User // 过滤敏感字段,不生成到Entity和VO中 List<String> ignoreColumns = Arrays.asList("password", "salt", "login_ip", "login_date"); strategy.setExclude(ignoreColumns.toArray(new String[0]));

同时,自定义FreeMarker模板(templates/entity.java.ftl)中增加字段注解控制:

<#list table.fields as field> <#if !ignoreColumns?seq_contains(field.name)> <#if field.keyFlag> @TableId(value = "${field.name}", type = IdType.AUTO) <#else> @TableField("${field.name}") </#if> private ${field.propertyType} ${field.propertyName}; </#if> </#list>

生成后,检查User.java中是否不含password、salt等字段——若存在,说明strategy.setExclude()未生效,需确认ignoreColumns变量在模板上下文中是否传入(实际开发中建议将过滤逻辑写死在模板内,避免运行时传参失败)。

3.3 编译与启动:解决Mapper XML路径与SQL语法兼容性问题

生成的UserMapper.xml默认放在resources/mapper/system/下,但若依的MyBatisConfig中mapperLocations配置为:

@Bean @Primary public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { MybatisSqlSessionFactoryBean factoryBean = new MybatisSqlSessionFactoryBean(); factoryBean.setDataSource(dataSource); factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver() .getResources("classpath:mapper/**/*.xml")); // 注意:是**/*.xml,非*/**.xml // ... }

因此,UserMapper.xml必须位于ruoyi-system/src/main/resources/mapper/system/,且<mapper namespace="com.ruoyi.system.mapper.UserMapper">中的namespace必须与接口全限定名一致。

常见翻车点:生成的XML中<select id="selectList">使用了Mybatis-Plus 3.4.3.4不支持的<bind>标签或<foreach>嵌套过深。此时需手动简化SQL:

<!-- 错误:MPG 3.4.1生成的复杂条件 --> <select id="selectList" resultType="com.ruoyi.system.domain.User"> SELECT * FROM sys_user WHERE del_flag = '0' <if test="userName != null and userName != ''"> AND user_name LIKE CONCAT('%', #{userName}, '%') </if> </select>

提示:若启动生成的Controller后调用/system/user/list返回空数组,但数据库有数据,大概率是XML中SQL语法不被Mybatis 3.4.3.4解析。此时打开application.yml,将logging.level.com.ruoyi.system.mapper=DEBUG,查看控制台打印的实际SQL,再对比数据库执行是否一致。

3.4 前端页面集成:复用若依Vue组件快速生成CRUD界面

若依前端使用Vue2 + Element UI,其列表页结构高度标准化。生成UserController后,无需从零写Vue,只需在ruoyi-ui/src/views/system/下新建user/index.vue,复用若依现有组件:

<template> <div class="app-container"> <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px"> <el-form-item label="用户名称" prop="userName"> <el-input v-model="queryParams.userName" placeholder="请输入用户名称" clearable @keyup.enter.native="handleQuery"/> </el-form-item> <el-form-item> <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button> <el-button type="danger" icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button> </el-form-item> </el-form> <el-table v-loading="loading" :data="userList" @sort-change="sortChange" style="width: 100%"> <el-table-column label="用户编号" align="center" prop="userId"/> <el-table-column label="登录名称" align="center" prop="loginName"/> <el-table-column label="用户昵称" align="center" prop="userName"/> <el-table-column label="邮箱" align="center" prop="email"/> <el-table-column label="手机号" align="center" prop="phonenumber"/> <el-table-column label="状态" align="center" prop="status"> <template slot-scope="scope"> <dict-tag :options="sys_normal_disable" :value="scope.row.status"/> </template> </el-table-column> <el-table-column label="操作" align="center" class-name="small-padding fixed-width"> <template slot-scope="scope"> <el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:user:edit']">修改</el-button> <el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)" v-hasPermi="['system:user:remove']">删除</el-button> </template> </el-table-column> </el-table> <pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList" /> </div> </template> <script> import { listUser, deleteUser } from "@/api/system/user"; export default { name: "User", data() { return { loading: true, total: 0, userList: [], queryParams: { pageNum: 1, pageSize: 10, userName: undefined }, showSearch: true, sys_normal_disable: [] }; }, created() { this.getList(); }, methods: { getList() { this.loading = true; listUser(this.queryParams).then(response => { this.userList = response.rows; this.total = response.total; this.loading = false; }); }, handleQuery() { this.queryParams.pageNum = 1; this.getList(); }, resetQuery() { this.$refs.queryForm.resetFields(); this.handleQuery(); }, handleUpdate(row) { this.$router.push({ path: '/system/user/edit', query: { userId: row.userId } }); }, handleDelete(row) { this.$confirm('是否确认删除用户编号为"' + row.userId + '"的数据项?', "警告", { confirmButtonText: "确定", cancelButtonText: "取消", type: "warning" }).then(function() { return deleteUser(row.userId); }).then(() => { this.getList(); this.$message.success("删除成功"); }).catch(function() {}); } } }; </script>

关键点:listUser、deleteUser等API函数需在ruoyi-ui/src/api/system/user.js中定义,且URL前缀必须为/system/user,与后端@RequestMapping("/system/user")严格对应。若前端报404,先检查api文件中URL是否漏写/system/。

3.5 权限控制验证:从前端按钮到后端接口的双重拦截

若依权限体系是双层拦截:前端通过v-hasPermi指令控制按钮显隐,后端通过@PreAuthorize注解校验权限。生成的UserController中已自带注解:

@RestController @RequestMapping("/system/user") public class UserController extends BaseController { @PreAuthorize("@ss.hasPermi('system:user:list')") @GetMapping("/list") public TableDataInfo list(User user) { ... } @PreAuthorize("@ss.hasPermi('system:user:add')") @PostMapping public AjaxResult add(@Validated @RequestBody User user) { ... } }

验证方式:

  • 用普通用户(非admin)登录,进入用户管理页,确认“新增”、“修改”按钮不显示(v-hasPermi生效);
  • 手动在浏览器控制台执行axios.get('/system/user/list'),返回403 Forbidden(后端@PreAuthorize拦截);
  • 用admin账号登录,执行相同请求,返回正常数据。

若前端按钮隐藏但后端未拦截(即普通用户F12改请求仍能调用),说明@PreAuthorize未生效——检查ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java中是否启用@EnableGlobalMethodSecurity(prePostEnabled = true),该注解必须存在且prePostEnabled = true。

3.6 菜单动态刷新:解决生成后前端菜单不显示的缓存问题

生成菜单数据后,前端不会自动刷新左侧菜单。学生常以为“数据写进数据库了,菜单就该出来”,结果答辩时导师点开系统,左侧还是老样子。

根本原因:若依前端菜单数据在登录时从/getRouters接口一次性加载并缓存于Vuex,后续不主动刷新。解决方案有两个:

方案A(推荐,适合毕设):强制登出再登录
生成菜单后,清空浏览器localStorage中ruoyi_token和ruoyi_menus,重新登录即可加载新菜单。

方案B(生产可用):调用/sys/menu/list接口实时刷新
在ruoyi-ui/src/layout/components/Sidebar/index.vue中,监听路由变化,当进入系统管理页时触发菜单刷新:

watch: { $route: { handler: function(val, oldVal) { if (val.path.startsWith('/system') && val.path !== oldVal.path) { this.$store.dispatch('permission/getRouters').then(() => { this.$nextTick(() => { this.$router.push(val.path); // 重新导航 }); }); } }, immediate: true } }

血泪经验:答辩前务必用非admin账号测试权限,用admin账号测试菜单刷新。曾有同学答辩时演示“新增用户”,结果点“新增”按钮没反应,排查半小时才发现v-hasPermi="system:user:add"写成了system:user:adds(多了一个s),这种低级错误在压力下极易发生。


4. 避坑指南:集成过程中最常翻车的5个致命点及修复方案

集成若依与Mybatis-Plus代码生成器,表面是加依赖、写配置,实则处处是坑。以下是我在多个毕设指导和企业项目中高频遇到的5个致命问题,按“现象→原因→解决”结构给出可立即执行的修复方案。

4.1 现象:生成器执行报java.lang.NoClassDefFoundError: com/baomidou/mybatisplus/core/toolkit/StringUtils

原因:mybatis-plus-generator:3.4.1依赖mybatis-plus-core:3.4.1,但若依工程中mybatis-plus-boot-starter:3.4.3.4又引入了mybatis-plus-core:3.4.3.4,两个版本的StringUtils类签名不一致(3.4.1中无isBlank(CharSequence)静态方法,3.4.3.4中有),导致类加载冲突。
解决:在ruoyi-admin/pom.xml中,为mybatis-plus-generator添加<exclusions>彻底排除其传递依赖,并确保mybatis-plus-core仅由mybatis-plus-boot-starter提供:

<dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-generator</artifactId> <version>3.4.1</version> <exclusions> <exclusion> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-core</artifactId> </exclusion> <exclusion> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-extension</artifactId> </exclusion> </exclusions> </dependency>

执行mvn clean compile后,用javap -cp target/classes com.baomidou.mybatisplus.generator.AutoGenerator确认类路径中无重复StringUtils。

4.2 现象:生成的Controller中@PreAuthorize注解不生效,所有用户都能访问

原因:若依的SecurityConfig中@EnableGlobalMethodSecurity注解缺失或prePostEnabled设为false,导致@PreAuthorize被Spring Security忽略。
解决:打开ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java,确认存在以下配置:

@Configuration @EnableGlobalMethodSecurity(prePostEnabled = true, proxyTargetClass = true) public class SecurityConfig extends WebSecurityConfigurerAdapter { ... }

特别注意proxyTargetClass = true——若设为false(JDK Proxy),则@PreAuthorize在@Service层不生效,必须设为true启用CGLIB代理。

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

原因:MPG生成的UserController未被Spring MVC扫描到,常见于两种情况:(1)@RestController注解被误删;(2)ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/包未被@ComponentScan包含。
解决:检查ruoyi-admin/src/main/java/com/ruoyi/RuoYiApplication.java中的@ComponentScan:

@SpringBootApplication @ComponentScan(basePackages = {"com.ruoyi"}) public class RuoYiApplication { ... }

确保basePackages包含com.ruoyi.web.controller.system。若为{"com.ruoyi.admin"},则需改为{"com.ruoyi"}或显式添加"com.ruoyi.web.controller.system"。

4.4 现象:生成的UserMapper.xml中SQL执行报org.apache.ibatis.binding.BindingException: Invalid bound statement (not found)

原因:UserMapper.xml中的namespace与UserMapper.java接口全限定名不一致,或SqlSessionFactory未扫描到该XML文件。
解决:

  1. 检查UserMapper.xml首行:<mapper namespace="com.ruoyi.system.mapper.UserMapper">,必须与接口类名完全一致;
  2. 检查ruoyi-system/src/main/resources/mapper/system/UserMapper.xml路径是否正确(不能是mapper/UserMapper.xml);
  3. 检查MyBatisConfig.java中setMapperLocations()是否包含classpath:mapper/**/*.xml(注意是**,非*)。

4.5 现象:生成的页面中“新增”按钮点击后跳转空白页,控制台报Cannot find module '@/views/system/user/add'

原因:MPG只生成后端代码,前端add.vue、edit.vue页面需手动创建,但学生常忘记创建,或创建路径错误(如放在views/user/add.vue而非views/system/user/add.vue)。
解决:

  1. 在ruoyi-ui/src/views/system/user/下新建add.vue和edit.vue;
  2. add.vue中<script>部分import { addUser } from "@/api/system/user",确保API路径与后端@PostMapping路径一致;
  3. edit.vue中this.userId = this.$route.query.userId获取参数,mounted()中调用getUser(this.userId)加载数据。

注意:若依前端路由配置在ruoyi-ui/src/router/index.js中,system模块路由已预定义,无需额外添加,但user子路由需在children中声明(若未声明,则/system/user/add无法匹配)。检查该文件中是否有:

{ path: 'user', component: () => import('@/views/system/user/index'), name: 'User', meta: { title: '用户管理', icon: 'peoples' } }

5. 毕设答辩验收:从代码到演示的5个必过检查点与答辩话术

毕设答辩不是考你背了多少原理,而是看你能不能把一套东西从零跑通、讲清楚、防住坑。我带过十几届学生,发现只要把以下5个检查点提前准备好,答辩通过率接近100%。它们不是“加分项”,而是评委老师一定会问的“底线问题”。下面给出每个点的自查清单、演示脚本和应对话术,全部来自真实答辩现场。

5.1 检查点①:生成器能否独立运行?——证明你不是抄的代码

自查清单:

  • ✅ 在ruoyi-admin模块下,存在CodeGenerator.java主类;
  • ✅ 运行该类main方法,控制台输出[INFO] 文件生成成功,且ruoyi-system目录下出现domain/User.java等文件;
  • ✅ 修改CodeGenerator.java中strategy.setInclude("sys_dept"),重新运行,生成Dept.java等新文件,无报错。

演示脚本:

  1. 打开IDEA,定位到CodeGenerator.java;
  2. 右键→Run CodeGenerator.main();
  3. 切换到ruoyi-system/src/main/java/com/ruoyi/system/domain/,展开文件树,指出User.java和Dept.java;
  4. 打开User.java,滚动到@TableName("sys_user")和@TableField("user_name"),说明“下划线转驼峰已生效”。

答辩话术:

“老师好,我先演示代码生成器的核心能力。它不是一个静态模板,而是可配置、可复用的工具。比如这里我把表名从sys_user改成sys_dept,运行后立刻生成部门管理的全套代码,包括实体类、Mapper、Service和Controller。这说明我对Mybatis-Plus生成器的API调用是准确的,不是简单复制粘贴。”

5.2 检查点②:生成的代码能否编译通过?——证明你解决了依赖冲突

自查清单:

  • ✅

本文还有配套的精品资源,点击获取

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

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

立即咨询