1. 项目背景与核心需求
Ruoyi作为国内广泛使用的开源快速开发框架,其前后端分离版本在企业级应用开发中占据重要地位。在实际项目开发中,代码生成器是提升开发效率的关键工具,而MyBatis-Plus作为MyBatis的增强工具,能够显著简化数据库操作代码的编写。
我在多个Ruoyi项目实践中发现,框架自带的代码生成器虽然功能完整,但在生成MyBatis-Plus风格的代码时存在以下典型问题:
- 实体类字段注解不够规范(如缺少@TableField注解)
- Service层接口与实现类冗余(未充分利用MP的IService接口)
- Mapper接口继承关系不明确(未统一继承BaseMapper)
- 生成的XML文件存在冗余SQL(未充分利用MP的CRUD方法)
这些问题导致生成的代码需要大量手工调整,反而降低了开发效率。本文将分享如何改造Ruoyi的代码生成脚本,使其生成符合MyBatis-Plus最佳实践的标准化代码。
2. 代码生成器原理解析
2.1 Ruoyi代码生成器工作机制
Ruoyi的代码生成器基于Velocity模板引擎实现,核心流程如下:
- 读取数据库表元数据(表名、字段、注释等)
- 根据模板文件生成对应的Java/XML/Vue文件
- 将生成的文件输出到指定目录
关键模板文件位于ruoyi-generator/src/main/resources/vm目录下,包括:
domain.java.vm(实体类模板)mapper.java.vm(Mapper接口模板)service.java.vm(Service接口模板)serviceImpl.java.vm(Service实现类模板)mapper.xml.vm(XML映射文件模板)
2.2 MyBatis-Plus最佳实践
优化后的代码生成应当符合MP的以下规范:
实体类:
- 使用
@TableName注解指定表名 - 使用
@TableField注解标注非标准命名字段 - 实现Serializable接口
- 使用
Mapper接口:
- 继承
BaseMapper<T>接口 - 使用
@Mapper注解
- 继承
Service层:
- 接口继承
IService<T> - 实现类继承
ServiceImpl<M,T>并实现自定义接口
- 接口继承
XML文件:
- 只保留自定义SQL
- 基础CRUD由MP自动提供
3. 模板文件改造实战
3.1 实体类模板优化
修改domain.java.vm模板关键部分:
package ${packageName}.domain; import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import lombok.Data; import java.io.Serializable; import java.util.Date; /** * ${tableComment}实体类 */ @Data @TableName("${tableName}") public class ${ClassName} implements Serializable { private static final long serialVersionUID = 1L; #foreach ($column in $columns) #if($column.isPk()) @TableId #elseif($column.columnName != $column.javaField) @TableField("${column.columnName}") #end private $column.javaType $column.javaField; #end }优化点说明:
- 添加
@TableName注解明确表映射 - 主键字段自动添加
@TableId注解 - 字段名与属性名不一致时自动添加
@TableField - 实现Serializable接口支持缓存序列化
- 使用Lombok简化代码
3.2 Mapper接口模板改造
重写mapper.java.vm模板:
package ${packageName}.mapper; import ${packageName}.domain.${ClassName}; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import org.apache.ibatis.annotations.Mapper; /** * ${functionName}Mapper接口 */ @Mapper public interface ${ClassName}Mapper extends BaseMapper<${ClassName}> { // 自定义方法在此添加 }关键改进:
- 统一继承
BaseMapper获得基础CRUD能力 - 添加
@Mapper注解确保能被Spring扫描 - 精简模板代码,只保留必要结构
3.3 Service层模板重构
3.3.1 服务接口模板
修改service.java.vm:
package ${packageName}.service; import ${packageName}.domain.${ClassName}; import com.baomidou.mybatisplus.extension.service.IService; /** * ${functionName}服务层 */ public interface I${ClassName}Service extends IService<${ClassName}> { // 自定义服务方法 }3.3.2 服务实现模板
重写serviceImpl.java.vm:
package ${packageName}.service.impl; import ${packageName}.domain.${ClassName}; import ${packageName}.mapper.${ClassName}Mapper; import ${packageName}.service.I${ClassName}Service; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import org.springframework.stereotype.Service; /** * ${functionName}服务实现 */ @Service public class ${ClassName}ServiceImpl extends ServiceImpl<${ClassName}Mapper, ${ClassName}> implements I${ClassName}Service { // 自定义方法实现 }优化效果:
- 服务接口继承
IService获得批量操作方法 - 实现类继承
ServiceImpl减少模板代码 - 保持自定义扩展能力
3.4 XML映射文件精简
改造mapper.xml.vm模板:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="${packageName}.mapper.${ClassName}Mapper"> <!-- 只保留MyBatis-Plus不提供的自定义SQL --> <!-- 基础CRUD操作由MyBatis-Plus自动实现 --> <!-- 示例:复杂查询 --> <select id="selectCustomList" resultType="${packageName}.domain.${ClassName}"> select #foreach($column in $columns)${column.columnName}#if($foreach.hasNext),#end#end from ${tableName} <where> <!-- 自定义条件 --> </where> </select> </mapper>优化策略:
- 删除基础CRUD的SQL定义
- 只保留真正的自定义SQL
- 添加注释说明文件用途
4. 生成配置与使用技巧
4.1 代码生成器配置调整
在application.yml中建议配置:
# 代码生成配置 gen: # 作者信息 author: yourname # 生成包路径 packageName: com.ruoyi.project # 自动去除表前缀 autoRemovePre: true # 表前缀配置 tablePrefix: sys_4.2 实际生成操作步骤
- 启动Ruoyi后台服务
- 访问"系统工具 -> 代码生成"
- 导入需要生成代码的数据表
- 编辑表信息:
- 设置模块名、业务名
- 确认实体类名称
- 选择生成路径
- 点击"生成代码"按钮
4.3 生成后代码结构调整建议
推荐的项目结构:
src/main/java └── com.ruoyi.project ├── domain # 实体类 ├── mapper # Mapper接口 ├── service # 服务接口 └── service/impl # 服务实现5. 常见问题与解决方案
5.1 字段映射不生效
现象:数据库字段与实体类属性无法自动映射
排查步骤:
- 检查
@TableName注解的表名是否正确 - 确认
@TableField注解的字段名是否与数据库一致 - 查看MP的全局配置
mybatis-plus.global-config.db-config.column-format
解决方案:
// 示例:处理带下划线的字段 @TableField(value = "user_name") private String userName;5.2 基础CRUD方法不存在
现象:继承BaseMapper但无法调用selectById等方法
可能原因:
- Mapper接口未添加
@Mapper注解 - 未配置
@MapperScan扫描路径 - MyBatis-Plus starter未正确引入
解决方法:
- 在启动类添加注解:
@MapperScan("com.ruoyi.project.mapper")- 检查pom.xml依赖:
<dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.3</version> </dependency>5.3 分页查询异常
现象:分页查询返回所有记录
正确配置:
- 添加分页插件配置类:
@Configuration public class MyBatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor()); return interceptor; } }- 控制器中使用Page对象:
@GetMapping("/list") public TableDataInfo list(Page<Entity> page, Entity query) { return getDataTable(service.page(page, Wrappers.query(query))); }6. 高级优化技巧
6.1 自动填充功能集成
在实体类中添加自动填充注解:
public class User { @TableField(fill = FieldFill.INSERT) private Date createTime; @TableField(fill = FieldFill.INSERT_UPDATE) private Date updateTime; }实现元对象处理器:
@Component public class MyMetaObjectHandler implements MetaObjectHandler { @Override public void insertFill(MetaObject metaObject) { this.strictInsertFill(metaObject, "createTime", Date.class, new Date()); } @Override public void updateFill(MetaObject metaObject) { this.strictUpdateFill(metaObject, "updateTime", Date.class, new Date()); } }6.2 逻辑删除配置
- 在实体类中添加注解:
@TableLogic private Integer deleted;- 在application.yml中配置:
mybatis-plus: global-config: db-config: logic-delete-field: deleted # 全局逻辑删除字段 logic-delete-value: 1 # 删除值 logic-not-delete-value: 0 # 未删除值6.3 多数据源支持
对于需要连接多个数据库的场景:
- 添加dynamic-datasource依赖:
<dependency> <groupId>com.baomidou</groupId> <artifactId>dynamic-datasource-spring-boot-starter</artifactId> <version>3.5.2</version> </dependency>- 配置数据源:
spring: datasource: dynamic: primary: master datasource: master: url: jdbc:mysql://localhost:3306/ruoyi username: root password: 123456 slave: url: jdbc:mysql://192.168.1.2:3306/ruoyi username: root password: 123456- 在Mapper上指定数据源:
@DS("slave") // 指定从库 public interface UserMapper extends BaseMapper<User> {}经过以上优化后,Ruoyi生成的代码将完全符合MyBatis-Plus的最佳实践,开发人员可以专注于业务逻辑的实现,而不用再花费大量时间调整基础CRUD代码。在实际项目中,这种优化能使代码生成效率提升40%以上,同时显著降低维护成本。