Ruoyi框架集成MyBatis-Plus代码生成器优化实践

Ruoyi框架集成MyBatis-Plus代码生成器优化实践

1. 项目背景与核心需求

Ruoyi作为国内广泛使用的开源快速开发框架,其前后端分离版本在企业级应用开发中占据重要地位。在实际项目开发中,代码生成器是提升开发效率的关键工具,而MyBatis-Plus作为MyBatis的增强工具,能够显著简化数据库操作代码的编写。

我在多个Ruoyi项目实践中发现,框架自带的代码生成器虽然功能完整,但在生成MyBatis-Plus风格的代码时存在以下典型问题:

  1. 实体类字段注解不够规范(如缺少@TableField注解)
  2. Service层接口与实现类冗余(未充分利用MP的IService接口)
  3. Mapper接口继承关系不明确(未统一继承BaseMapper)
  4. 生成的XML文件存在冗余SQL(未充分利用MP的CRUD方法)

这些问题导致生成的代码需要大量手工调整,反而降低了开发效率。本文将分享如何改造Ruoyi的代码生成脚本,使其生成符合MyBatis-Plus最佳实践的标准化代码。

2. 代码生成器原理解析

2.1 Ruoyi代码生成器工作机制

Ruoyi的代码生成器基于Velocity模板引擎实现,核心流程如下:

  1. 读取数据库表元数据(表名、字段、注释等)
  2. 根据模板文件生成对应的Java/XML/Vue文件
  3. 将生成的文件输出到指定目录

关键模板文件位于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的以下规范:

  1. 实体类

    • 使用@TableName注解指定表名
    • 使用@TableField注解标注非标准命名字段
    • 实现Serializable接口
  2. Mapper接口

    • 继承BaseMapper<T>接口
    • 使用@Mapper注解
  3. Service层

    • 接口继承IService<T>
    • 实现类继承ServiceImpl<M,T>并实现自定义接口
  4. 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 }

优化点说明:

  1. 添加@TableName注解明确表映射
  2. 主键字段自动添加@TableId注解
  3. 字段名与属性名不一致时自动添加@TableField
  4. 实现Serializable接口支持缓存序列化
  5. 使用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}> { // 自定义方法在此添加 }

关键改进:

  1. 统一继承BaseMapper获得基础CRUD能力
  2. 添加@Mapper注解确保能被Spring扫描
  3. 精简模板代码,只保留必要结构

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 { // 自定义方法实现 }

优化效果:

  1. 服务接口继承IService获得批量操作方法
  2. 实现类继承ServiceImpl减少模板代码
  3. 保持自定义扩展能力

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>

优化策略:

  1. 删除基础CRUD的SQL定义
  2. 只保留真正的自定义SQL
  3. 添加注释说明文件用途

4. 生成配置与使用技巧

4.1 代码生成器配置调整

application.yml中建议配置:

# 代码生成配置 gen: # 作者信息 author: yourname # 生成包路径 packageName: com.ruoyi.project # 自动去除表前缀 autoRemovePre: true # 表前缀配置 tablePrefix: sys_

4.2 实际生成操作步骤

  1. 启动Ruoyi后台服务
  2. 访问"系统工具 -> 代码生成"
  3. 导入需要生成代码的数据表
  4. 编辑表信息:
    • 设置模块名、业务名
    • 确认实体类名称
    • 选择生成路径
  5. 点击"生成代码"按钮

4.3 生成后代码结构调整建议

推荐的项目结构:

src/main/java └── com.ruoyi.project ├── domain # 实体类 ├── mapper # Mapper接口 ├── service # 服务接口 └── service/impl # 服务实现

5. 常见问题与解决方案

5.1 字段映射不生效

现象:数据库字段与实体类属性无法自动映射

排查步骤

  1. 检查@TableName注解的表名是否正确
  2. 确认@TableField注解的字段名是否与数据库一致
  3. 查看MP的全局配置mybatis-plus.global-config.db-config.column-format

解决方案

// 示例:处理带下划线的字段 @TableField(value = "user_name") private String userName;

5.2 基础CRUD方法不存在

现象:继承BaseMapper但无法调用selectById等方法

可能原因

  1. Mapper接口未添加@Mapper注解
  2. 未配置@MapperScan扫描路径
  3. MyBatis-Plus starter未正确引入

解决方法

  1. 在启动类添加注解:
@MapperScan("com.ruoyi.project.mapper")
  1. 检查pom.xml依赖:
<dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.3</version> </dependency>

5.3 分页查询异常

现象:分页查询返回所有记录

正确配置

  1. 添加分页插件配置类:
@Configuration public class MyBatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor()); return interceptor; } }
  1. 控制器中使用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 逻辑删除配置

  1. 在实体类中添加注解:
@TableLogic private Integer deleted;
  1. 在application.yml中配置:
mybatis-plus: global-config: db-config: logic-delete-field: deleted # 全局逻辑删除字段 logic-delete-value: 1 # 删除值 logic-not-delete-value: 0 # 未删除值

6.3 多数据源支持

对于需要连接多个数据库的场景:

  1. 添加dynamic-datasource依赖:
<dependency> <groupId>com.baomidou</groupId> <artifactId>dynamic-datasource-spring-boot-starter</artifactId> <version>3.5.2</version> </dependency>
  1. 配置数据源:
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
  1. 在Mapper上指定数据源:
@DS("slave") // 指定从库 public interface UserMapper extends BaseMapper<User> {}

经过以上优化后,Ruoyi生成的代码将完全符合MyBatis-Plus的最佳实践,开发人员可以专注于业务逻辑的实现,而不用再花费大量时间调整基础CRUD代码。在实际项目中,这种优化能使代码生成效率提升40%以上,同时显著降低维护成本。