【RuoYi-SpringBoot3-Pro】:MyBatis-Plus 集成

简介: RuoYi-SpringBoot3-Pro 集成 MyBatis-Plus 3.5.12,提供分页、乐观锁、多租户、Lambda 查询等核心功能,支持动态条件构建与代码生成,提升开发效率,助力企业级 SaaS 应用快速开发。

【RuoYi-SpringBoot3-Pro】:MyBatis-Plus 集成

本文详细介绍 RuoYi-SpringBoot3-Pro 框架中 MyBatis-Plus 的集成方案,包括核心插件配置、多租户支持、Lambda 查询、代码生成等实战技巧。

一、概述

RuoYi-SpringBoot3-Pro 使用 MyBatis-Plus 3.5.12 替换原有的 MyBatis,提供更强大、更便捷的 ORM 能力。MyBatis-Plus 是 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。

1.1 核心优势

特性 说明
无侵入 只做增强不做改变,引入不会对现有工程产生影响
损耗小 启动即会自动注入基本 CRUD,性能基本无损耗
强大的 CRUD 内置通用 Mapper、Service,少量配置即可实现单表大部分 CRUD
Lambda 表达式 通过 Lambda 表达式,方便编写各类查询条件
主键自动生成 支持多种主键策略,可自由配置
内置分页插件 基于 MyBatis 物理分页,自动识别数据库类型
内置性能分析插件 可输出 SQL 语句及执行时间
内置全局拦截插件 提供全表 delete、update 操作智能分析阻断

1.2 项目依赖

<!-- MyBatis-Plus Spring Boot 3 Starter -->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
    <version>3.5.12</version>
</dependency>

<!-- MyBatis-Plus JSqlParser(SQL 解析器) -->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-jsqlparser</artifactId>
    <version>3.5.12</version>
</dependency>

二、核心配置

2.1 application.yml 配置

# MyBatis-Plus 配置
mybatis-plus:
  # 搜索指定包别名
  typeAliasesPackage: com.ruoyi.**.domain
  # 配置 mapper 的扫描,找到所有的 mapper.xml 映射文件
  mapperLocations: classpath*:mapper/**/*Mapper.xml
  # 加载全局的配置文件
  configLocation: classpath:mybatis/mybatis-config.xml

2.2 插件配置类

项目在 MybatisPlusConfig 中配置了四大核心插件:

@EnableTransactionManagement(proxyTargetClass = true)
@Configuration
@EnableConfigurationProperties(TenantProperties.class)
public class MybatisPlusConfig {
   

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(TenantProperties tenantProperties) {
   
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();

        // 1. 多租户插件(可选)
        if (Boolean.TRUE.equals(tenantProperties.getEnable())) {
   
            interceptor.addInnerInterceptor(
                new TenantLineInnerInterceptor(new MultiTenantHandler(tenantProperties))
            );
        }

        // 2. 分页插件
        interceptor.addInnerInterceptor(paginationInnerInterceptor());

        // 3. 乐观锁插件
        interceptor.addInnerInterceptor(optimisticLockerInnerInterceptor());

        // 4. 防全表更新删除插件
        interceptor.addInnerInterceptor(blockAttackInnerInterceptor());

        return interceptor;
    }
}

三、核心插件详解

3.1 分页插件

自动识别数据库类型,无需手动配置,支持 MySQL、PostgreSQL、Oracle、达梦、瀚高等多种数据库。

/**
 * 分页插件配置
 */
public PaginationInnerInterceptor paginationInnerInterceptor() {
   
    PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();
    // 可选:设置数据库类型(不设置则自动识别)
    // paginationInnerInterceptor.setDbType(DbType.MYSQL);
    // 可选:设置最大单页限制数量,默认 500 条,-1 不受限制
    // paginationInnerInterceptor.setMaxLimit(-1L);
    return paginationInnerInterceptor;
}

使用示例:

// Controller 层
@GetMapping("/page")
public TableDataInfo page(Region region) {
   
    // 获取分页对象
    Page<Region> page = getPage();
    // 获取查询条件
    QueryWrapper<Region> queryWrapper = getQueryWrapper(Region.class);
    // 执行分页查询
    IPage<Region> result = regionService.pageRegion(page, queryWrapper);
    return getDataTableByPage(result);
}

// Service 层
@Override
public IPage<Region> pageRegion(Page<Region> page, QueryWrapper<Region> queryWrapper) {
   
    return regionMapper.selectPage(page, queryWrapper);
}

3.2 乐观锁插件

防止并发修改导致数据丢失,通过版本号机制实现。

/**
 * 乐观锁插件配置
 */
public OptimisticLockerInnerInterceptor optimisticLockerInnerInterceptor() {
   
    return new OptimisticLockerInnerInterceptor();
}

实体类配置:

@Data
@TableName("biz_order")
public class Order {
   
    @TableId(type = IdType.AUTO)
    private Long id;

    private String orderNo;

    // 乐观锁版本号字段
    @Version
    private Integer version;
}

使用示例:

// 更新时自动检查版本号
Order order = orderService.getById(1L);
order.setOrderNo("NEW_ORDER_NO");
// 如果版本号不匹配,更新失败返回 0
int rows = orderMapper.updateById(order);

3.3 防全表更新删除插件

避免误操作造成数据丢失,当执行不带 WHERE 条件的 UPDATE 或 DELETE 时,会抛出异常。

/**
 * 防全表更新删除插件
 */
public BlockAttackInnerInterceptor blockAttackInnerInterceptor() {
   
    return new BlockAttackInnerInterceptor();
}

拦截示例:

// ❌ 以下操作会被拦截并抛出异常
orderMapper.delete(null);  // 全表删除
orderMapper.update(order, null);  // 全表更新

// ✅ 正确的操作方式
QueryWrapper<Order> wrapper = new QueryWrapper<>();
wrapper.eq("status", "CANCELLED");
orderMapper.delete(wrapper);  // 带条件删除

3.4 多租户插件

企业级 SaaS 应用必备能力,自动为 SQL 添加租户条件。

配置文件:

# 多租户配置
tenant:
  # 是否启用多租户
  enable: true
  # 租户 ID 字段名
  column: tenant_id
  # 需要过滤的表(可选)
  filterTables:
  # 忽略多租户的表
  ignoreTables:
    - sys_config
    - sys_dict_data
    - sys_dict_type
    - sys_menu
    # ... 其他系统表
  # 忽略多租户的用户(如超级管理员)
  ignoreLoginNames:
    - admin

多租户处理器:

public class MultiTenantHandler implements TenantLineHandler {
   
    private final TenantProperties properties;

    @Override
    public Expression getTenantId() {
   
        // 从当前登录用户获取租户 ID
        return new LongValue(SecurityUtils.getLoginUser().getUser().getTenantId());
    }

    @Override
    public String getTenantIdColumn() {
   
        return properties.getColumn();  // 返回 "tenant_id"
    }

    @Override
    public boolean ignoreTable(String tableName) {
   
        // 判断是否忽略该表
        List<String> ignoreTables = properties.getIgnoreTables();
        return ignoreTables != null && ignoreTables.contains(tableName);
    }
}

效果示例:

-- 原始 SQL
SELECT * FROM biz_order WHERE status = 'PAID'

-- 自动添加租户条件后
SELECT * FROM biz_order WHERE status = 'PAID' AND tenant_id = 1001

四、实体类注解

4.1 常用注解说明

@Data
@TableName("biz_region")  // 指定表名
public class Region implements Serializable {
   
    private static final long serialVersionUID = 1L;

    /**
     * 主键
     */
    @TableId(type = IdType.AUTO)  // 主键策略:自增
    @OrderBy(asc = true, sort = 1)  // 默认排序
    private String id;

    /**
     * 上级 ID
     */
    @TableField("parent_id")  // 指定字段名
    private String parentId;

    /**
     * 名称
     */
    @TableField("name")
    private String name;

    /**
     * 层级
     */
    @TableField("level")
    private Integer level;

    /**
     * 非数据库字段
     */
    @TableField(exist = false)  // 标记为非数据库字段
    private List<Region> children;
}

4.2 主键策略

策略 说明
IdType.AUTO 数据库自增
IdType.NONE 无状态,跟随全局配置
IdType.INPUT 手动输入
IdType.ASSIGN_ID 雪花算法(默认)
IdType.ASSIGN_UUID UUID

五、Lambda 查询

MyBatis-Plus 提供了强大的 Lambda 查询能力,避免硬编码字段名,编译期即可发现错误。

5.1 LambdaQueryWrapper

// 传统方式(字段名硬编码,容易出错)
QueryWrapper<Region> wrapper = new QueryWrapper<>();
wrapper.eq("level", 1);
wrapper.like("name", "北京");

// Lambda 方式(类型安全,IDE 自动补全)
LambdaQueryWrapper<Region> lambdaWrapper = new LambdaQueryWrapper<>();
lambdaWrapper.eq(Region::getLevel, 1)
             .like(Region::getName, "北京")
             .orderByAsc(Region::getId);

List<Region> list = regionMapper.selectList(lambdaWrapper);

5.2 链式查询

// 链式 Lambda 查询
List<Region> provinces = regionService.lambdaQuery()
    .eq(Region::getLevel, 1)
    .orderByAsc(Region::getId)
    .list();

// 带条件的链式查询
String keyword = "北京";
List<Region> result = regionService.lambdaQuery()
    .eq(Region::getLevel, 1)
    .like(StringUtils.isNotEmpty(keyword), Region::getName, keyword)
    .list();

5.3 常用条件方法

方法 说明 示例
eq 等于 .eq(Region::getLevel, 1)
ne 不等于 .ne(Region::getLevel, 0)
gt 大于 .gt(Region::getLevel, 1)
ge 大于等于 .ge(Region::getLevel, 1)
lt 小于 .lt(Region::getLevel, 4)
le 小于等于 .le(Region::getLevel, 3)
like 模糊匹配 .like(Region::getName, "北")
likeLeft 左模糊 .likeLeft(Region::getName, "京")
likeRight 右模糊 .likeRight(Region::getName, "北")
between 区间 .between(Region::getLevel, 1, 3)
in IN 查询 .in(Region::getLevel, 1, 2, 3)
isNull 为空 .isNull(Region::getParentId)
isNotNull 不为空 .isNotNull(Region::getParentId)
orderByAsc 升序 .orderByAsc(Region::getId)
orderByDesc 降序 .orderByDesc(Region::getId)

六、动态查询工具

项目封装了 MybatisUtils 工具类,支持根据请求参数动态构建查询条件。

6.1 支持的查询后缀

后缀 说明 示例参数
Eq 等于 levelEq=1
Ne 不等于 statusNe=0
Gt 大于 levelGt=1
Ge 大于等于 levelGe=1
Lt 小于 levelLt=4
Le 小于等于 levelLe=3
Like 模糊匹配 nameLike=北京
LikeLeft 左模糊 nameLikeLeft=京
LikeRight 右模糊 nameLikeRight=北
Between 区间 levelBetween=1,3
In IN 查询 levelIn=1,2,3
IsNull 为空 parentIdIsNull
Asc 升序 idAsc
Desc 降序 idDesc

6.2 使用示例

// Controller 中使用
@GetMapping("/list")
public AjaxResult list() {
   
    // 自动根据请求参数构建查询条件
    QueryWrapper<Region> queryWrapper = getQueryWrapper(Region.class);
    return success(regionService.list(queryWrapper));
}

请求示例:

GET /biz/Region/list?levelEq=1&nameLike=北&idAsc

生成的 SQL:

SELECT * FROM biz_region 
WHERE level = 1 AND name LIKE '%北%' 
ORDER BY id ASC

七、Service 层封装

7.1 继承 IService

public interface IRegionService extends IService<Region> {
   
    // 自定义方法
    IPage<Region> pageRegion(Page<Region> page, QueryWrapper<Region> queryWrapper);
    String idsToNames(String ids);
}

7.2 继承 ServiceImpl

@Service
@RequiredArgsConstructor
public class RegionServiceImpl extends ServiceImpl<RegionMapper, Region> 
    implements IRegionService {
   

    private final RegionMapper regionMapper;

    @Override
    public IPage<Region> pageRegion(Page<Region> page, QueryWrapper<Region> queryWrapper) {
   
        return regionMapper.selectPage(page, queryWrapper);
    }

    // 使用继承的方法
    public void example() {
   
        // 查询单条
        Region region = this.getById(1L);

        // 查询列表
        List<Region> list = this.list();

        // 条件查询
        List<Region> provinces = this.lambdaQuery()
            .eq(Region::getLevel, 1)
            .list();

        // 保存
        this.save(new Region());

        // 批量保存
        this.saveBatch(list);

        // 更新
        this.updateById(region);

        // 删除
        this.removeById(1L);
    }
}

7.3 IService 常用方法

方法 说明
save(T entity) 插入一条记录
saveBatch(Collection<T>) 批量插入
saveOrUpdate(T entity) 存在则更新,否则插入
removeById(Serializable id) 根据 ID 删除
removeByIds(Collection<?>) 批量删除
updateById(T entity) 根据 ID 更新
getById(Serializable id) 根据 ID 查询
list() 查询所有
list(Wrapper<T>) 条件查询
page(IPage<T>, Wrapper<T>) 分页查询
count() 查询总数
lambdaQuery() Lambda 链式查询
lambdaUpdate() Lambda 链式更新

八、Mapper 层

8.1 继承 BaseMapper

public interface RegionMapper extends BaseMapper<Region> {
   
    /**
     * 自定义查询方法(使用 XML)
     */
    List<Region> selectRegionList(Region region);
}

8.2 BaseMapper 内置方法

方法 说明
insert(T entity) 插入一条记录
deleteById(Serializable id) 根据 ID 删除
deleteByIds(Collection<?>) 批量删除
updateById(T entity) 根据 ID 更新
selectById(Serializable id) 根据 ID 查询
selectBatchIds(Collection<?>) 批量查询
selectList(Wrapper<T>) 条件查询
selectPage(IPage<T>, Wrapper<T>) 分页查询
selectCount(Wrapper<T>) 查询总数

九、代码生成器适配

RuoYi-SpringBoot3-Pro 的代码生成器已针对 MyBatis-Plus 优化:

9.1 生成的实体类

@Data
@TableName("biz_order")
public class Order implements Serializable {
   
    private static final long serialVersionUID = 1L;

    @TableId(type = IdType.AUTO)
    private Long id;

    @Excel(name = "订单号")
    @TableField("order_no")
    private String orderNo;

    @Excel(name = "金额")
    @TableField("amount")
    private BigDecimal amount;

    @Excel(name = "创建时间", dateFormat = "yyyy-MM-dd HH:mm:ss")
    @TableField("create_time")
    private Date createTime;
}

9.2 生成的 Mapper

public interface OrderMapper extends BaseMapper<Order> {
   
    // 自动拥有 CRUD 方法,无需编写
}

9.3 生成的 Service

public interface IOrderService extends IService<Order> {
   
    // 业务方法
}

@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> 
    implements IOrderService {
   
    // 实现
}

十、最佳实践

10.1 分页查询标准写法

@GetMapping("/page")
public TableDataInfo page(Order order) {
   
    Page<Order> page = getPage();
    QueryWrapper<Order> wrapper = getQueryWrapper(Order.class);
    IPage<Order> result = orderService.page(page, wrapper);
    return getDataTableByPage(result);
}

10.2 条件构造器复用

// 封装通用查询条件
private LambdaQueryWrapper<Order> buildQueryWrapper(Order order) {
   
    return new LambdaQueryWrapper<Order>()
        .eq(order.getStatus() != null, Order::getStatus, order.getStatus())
        .like(StringUtils.isNotEmpty(order.getOrderNo()), Order::getOrderNo, order.getOrderNo())
        .ge(order.getStartTime() != null, Order::getCreateTime, order.getStartTime())
        .le(order.getEndTime() != null, Order::getCreateTime, order.getEndTime())
        .orderByDesc(Order::getCreateTime);
}

10.3 批量操作优化

// 批量插入(默认每批 1000 条)
orderService.saveBatch(orderList);

// 自定义批次大小
orderService.saveBatch(orderList, 500);

// 批量更新
orderService.updateBatchById(orderList);

10.4 逻辑删除配置

mybatis-plus:
  global-config:
    db-config:
      # 逻辑删除字段
      logic-delete-field: deleted
      # 逻辑已删除值
      logic-delete-value: 1
      # 逻辑未删除值
      logic-not-delete-value: 0
@Data
@TableName("biz_order")
public class Order {
   
    // ...

    @TableLogic
    private Integer deleted;
}

十一、常见问题

11.1 字段名与数据库列名不一致

使用 @TableField 注解指定:

@TableField("order_no")
private String orderNo;

11.2 忽略某个字段

@TableField(exist = false)
private String tempField;

11.3 自动填充

@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());
    }
}

十二、总结

RuoYi-SpringBoot3-Pro 的 MyBatis-Plus 集成方案具有以下特点:

  • 开箱即用:预配置分页、乐观锁、防误删等核心插件
  • 多租户支持:企业级 SaaS 应用必备能力
  • Lambda 查询:类型安全,告别硬编码
  • 动态查询:根据请求参数自动构建查询条件
  • 代码生成适配:生成的代码自动继承 BaseMapper 和 IService
  • 多数据库兼容:支持 MySQL、PostgreSQL、Oracle、达梦、瀚高等

目录
相关文章
|
XML 前端开发 Java
若依管理系统后端将 Mybatis 升级为 Mybatis-Plus
若依管理系统后端将 Mybatis 升级为 Mybatis-Plus
1798 0
|
2月前
|
SQL Java 数据库连接
MyBatis升级版:MyBatisPlus
MyBatisPlus是MyBatis的增强工具,简化单表CRUD操作,无需编写XML即可实现增删改查。通过继承BaseMapper、使用条件构造器(如QueryWrapper)、Service接口及分页插件,大幅提升开发效率。支持自定义SQL、逻辑删除、枚举与JSON处理,并提供代码生成器和通用分页封装,广泛应用于企业级项目中。
|
druid Java 数据库连接
Spring Boot3整合MyBatis Plus
Spring Boot3整合MyBatis Plus
1715 1
|
22天前
|
前端开发 JavaScript 安全
【RuoYi-SpringBoot3-ElementPlus】:若依前端增强版 —— 功能扩展优化
【RuoYi-SpringBoot3-ElementPlus】基于若依Vue3,100%兼容原功能,新增省市区级联、千分位输入、增强上传、UEditor等8+实用组件,集成dayjs、xe-utils、mitt等主流工具库,支持拖拽排序、移动端适配、三级等保自动登出,配备代码检查、Prettier格式化、FTP自动部署,开箱即用,显著提升开发效率与系统安全性。
333 1
|
22天前
|
NoSQL Java API
【RuoYi-SpringBoot3-Pro】:Magic API 低代码开发
RuoYi-SpringBoot3-Pro 集成 Magic API,实现低代码快速开发。通过 Web 界面编写脚本,无需编写 Controller、Service 等代码,支持实时生效、数据库操作、多数据源、权限校验与 Redis 缓存,助力高效构建 RESTful 接口,适用于原型开发、报表查询等场景。
326 0
|
5月前
|
XML Java Nacos
Spring Boot 整合Nacos 版本兼容适配 史上最详细文档
本文介绍SpringBoot整合Nacos的完整流程,涵盖Nacos下载安装、配置中心与服务发现集成、版本兼容性问题及实战配置。重点解决SpringBoot 3.3.0与Nacos版本适配难题,推荐使用Spring Cloud Alibaba方案,并提供项目开源地址供参考学习。
|
21天前
|
移动开发 小程序 JavaScript
【RuoYi-SpringBoot3-UniApp】:一套代码,多端运行的移动端开发方案
RuoYi-SpringBoot3-UniApp 是基于 Vue3 与 UniApp 的跨平台移动端解决方案,支持一套代码编译到小程序、App、H5 和桌面端。集成 Pinia 状态管理、JWT 认证、权限路由、z-paging 分页、mp-html 富文本等主流功能,开箱即用,显著降低多端开发与维护成本,助力高效构建企业级应用。
243 6
|
Java 数据库连接 开发者
Mybatis Plus公共字段自动填充(MyMetaObjectHandler)
Mybatis Plus公共字段自动填充(MyMetaObjectHandler)
1106 0
|
9月前
|
SQL Java 数据安全/隐私保护
发现问题:Mybatis-plus的分页总数为0,分页功能失效,以及多租户插件的使用。
总的来说,使用 Mybatis-plus 确实可以极大地方便我们的开发,但也需要我们理解其工作原理,掌握如何合适地使用各种插件。分页插件和多租户插件是其中典型,它们的运用可以让我们的代码更为简洁、高效,理解和掌握好它们的用法对我们的开发过程有着极其重要的意义。
843 15
|
人工智能 自然语言处理 Java
FastExcel:开源的 JAVA 解析 Excel 工具,集成 AI 通过自然语言处理 Excel 文件,完全兼容 EasyExcel
FastExcel 是一款基于 Java 的高性能 Excel 处理工具,专注于优化大规模数据处理,提供简洁易用的 API 和流式操作能力,支持从 EasyExcel 无缝迁移。
3274 65
FastExcel:开源的 JAVA 解析 Excel 工具,集成 AI 通过自然语言处理 Excel 文件,完全兼容 EasyExcel