当前位置: 首页 > news >正文

MyBatis Plus功能增强全解析:从手写SQL到优雅开发的进阶指南

引言

作为Java开发圈的“效率神器”,MyBatis Plus(以下简称MP)凭借“简化CRUD”的核心优势,早已成为无数项目的标配。但你知道吗?除了基础的baseMapper提供的增删改查,MP还藏着自动填充、逻辑删除、乐观锁等超实用增强功能,能帮你彻底摆脱重复代码,把精力聚焦在业务逻辑上!

今天,笔者从场景痛点→实现方案→避坑指南三个维度,带你深度解锁MP的CRUD增强能力,看完直接上手用!

一、自动填充:告别手写setCreateTime()的噩梦

场景痛点

相信90%的开发者都经历过:新增一条记录时,要手动给createTimeupdateTimecreateBy这些字段赋值;更新时,又要记得更新updateTime——稍不留神漏了某个字段,要么报空指针,要么数据不完整。

MP方案:@TableField(fill) + MetaObjectHandler

MP的自动填充功能,能让你彻底和“手写赋值”说拜拜!核心步骤就两步:

1. 实体类打标:告诉MP哪些字段需要自动填充

@TableField(fill = FieldFill.XXX)注解标记字段,MP会根据fill值自动判断填充时机:

  • FieldFill.DEFAULT:默认不填充(除非自定义)
  • FieldFill.INSERT:仅在插入时填充
  • FieldFill.UPDATE:仅在更新时填充
  • FieldFill.INSERT_UPDATE:插入和更新时都填充

示例代码:

@Data
@TableName("sys_user")
public class User {private Long id;private String username;// 插入时自动填充(默认当前时间)@TableField(fill = FieldFill.INSERT)private LocalDateTime createTime;// 插入和更新时都填充(比如最后修改时间)@TableField(fill = FieldFill.INSERT_UPDATE)private LocalDateTime updateTime;// 插入时自动填充当前用户ID(动态值)@TableField(fill = FieldFill.INSERT)private Long createBy;
}
2. 实现MetaObjectHandler:定义填充逻辑

MP会自动调用这个接口的方法,帮你把值“塞”进SQL参数里。只需重写insertFill(插入时触发)和updateFill(更新时触发)即可。

示例代码:

@Component // 必须注册为Bean!
public class MyMetaObjectHandler implements MetaObjectHandler {// 插入时填充(自动给createTime、updateTime、createBy赋值)@Overridepublic void insertFill(MetaObject metaObject) {// 获取当前时间(支持LocalDateTime、Date等)LocalDateTime now = LocalDateTime.now();this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, now);this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class, now);// 动态获取当前用户ID(比如从SecurityUtils中取)Long currentUserId = SecurityUtils.getCurrentUserId();this.strictInsertFill(metaObject, "createBy", Long.class, currentUserId);}// 更新时填充(自动更新updateTime和updateBy)@Overridepublic void updateFill(MetaObject metaObject) {LocalDateTime now = LocalDateTime.now();this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, now);Long currentUserId = SecurityUtils.getCurrentUserId();this.strictUpdateFill(metaObject, "updateBy", Long.class, currentUserId);}
}

⚠️ 避坑指南

  • 严格模式 vs 非严格模式strictInsertFill默认是严格模式(仅当字段为null时填充),如果想强制覆盖已有值(比如更新时不管是否为空都填),用fillStrategy方法:
    this.fillStrategy(metaObject, "updateTime", now); // 非严格模式,直接覆盖
    
  • 字段名别写错metaObject里的字段名要和实体类的属性名一致(不是数据库列名!)。

二、逻辑删除:从“物理删库”到“温柔删数据”的救赎

场景痛点

传统物理删除(DELETE FROM user WHERE id=1)虽然简单,但一旦手滑删错数据,哭都来不及!业务系统中,“删除”更多是“标记为不可用”,比如用户注销账号,但订单记录还得保留——这时候逻辑删除就派上用场了!

MP方案:@TableLogic + 全局配置

MP的逻辑删除功能,通过一个标记字段(如is_deleted)实现:查询时自动过滤已删除数据,删除操作自动转为更新标记。

1. 实体类打标:标记逻辑删除字段

@TableLogic注解标记哪个字段是逻辑删除的“开关”:

@Data
@TableName("sys_user")
public class User {private Long id;private String username;// 逻辑删除字段(0=未删除,1=已删除)@TableLogicprivate Integer isDeleted;
}
2. 全局配置:告诉MP逻辑删除的规则

application.yml里配置逻辑删除的字段名、未删除值和已删除值,MP会自动帮你处理查询过滤和SQL转换。

配置示例:

mybatis-plus:global-config:db-config:logic-delete-field: is_deleted  # 逻辑删除字段名(对应实体类的isDeleted)logic-not-delete-value: 0       # 未删除时的值(查询时自动加WHERE is_deleted=0)logic-delete-value: 1           # 已删除时的值(删除操作自动更新为1)

效果验证:不用改代码,CRUD自动生效!

  • 查询自动过滤:执行userMapper.selectById(1L)时,MP会自动拼接AND is_deleted=0,已删除的数据根本查不到!
  • 删除变更新:执行userMapper.deleteById(1L)时,实际执行的是UPDATE sys_user SET is_deleted=1 WHERE id=1,数据还在,只是被“软删除”了。
  • 分页自动排除:分页查询结果不会包含已删除数据,总条数也是过滤后的数量,省心!

⚠️ 避坑指南

  • 历史数据迁移:如果数据库已有存量数据,需要提前初始化is_deleted字段(未删除的设为0,已删除的设为1)。
  • 关联表处理:如果删除主表数据时需要级联删除关联表(如用户删除时删掉订单),逻辑删除无法自动实现,得自己写SQL或用MP的@TableLogic扩展。
  • 多租户扩展:如果同时需要逻辑删除和多租户隔离(比如每个租户只能看自己的数据),可以结合@TableField(condition = SqlCondition.LIKE)@TableLogic一起用。

三、乐观锁:高并发下的“防覆盖神器”

场景痛点

电商大促时,两个用户同时抢同一件商品,库存只剩1件。如果两个请求同时读取到库存为1,都去扣减,结果库存变成-1——这就是典型的并发覆盖问题!传统解决方案是用数据库行锁,但性能差;MP的乐观锁能让你用更轻量的方式解决这个问题。

MP方案:@Version + 乐观锁拦截器

乐观锁的核心思想是:“我认为数据不会被其他人修改,所以先读版本号,更新时检查版本号是否变化”。如果版本号变了,说明数据被其他人改过,更新失败。

1. 实体类打标:标记版本号字段

@Version注解标记一个版本号字段(初始值为0),MP会在更新时自动校验这个字段。

示例代码:

@Data
@TableName("sys_product")
public class Product {private Long id;private String name;private Integer stock;  // 库存@Version // 乐观锁版本号字段(初始0)private Integer version;
}
2. 配置拦截器:激活乐观锁功能

在Spring Boot的配置类里,添加OptimisticLockerInnerInterceptor拦截器,MP会自动帮你处理版本号校验。

配置示例:

@Configuration
public class MyBatisPlusConfig {@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();// 添加乐观锁拦截器interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());return interceptor;}
}
3. 使用示例:更新时自动校验版本号
// 1. 查询商品信息(此时version=0)
Product product = productMapper.selectById(1L);// 2. 扣减库存(假设卖1件)
product.setStock(product.getStock() - 1);// 3. 更新时,MP自动添加version条件:WHERE id=1 AND version=0
int rows = productMapper.updateById(product);if (rows == 0) {// 更新失败(版本号不匹配,说明数据被其他人修改过)throw new RuntimeException("库存已被其他用户修改,请刷新重试!");
}

⚠️ 避坑指南

  • 仅支持主键更新:乐观锁只在updateByIdUpdateWrapper中显式指定主键时生效。如果用update方法但不带主键条件,版本号校验会失效!
  • 版本号类型:推荐用IntegerLong,避免用String(比较效率低)。
  • 批量更新不生效:乐观锁仅适用于单条记录的更新,批量更新(如同时更新10条)需要自己处理版本号。

四、批量操作:告别“逐条SQL”的性能灾难

场景痛点

做Excel数据导入时,如果有1000条数据,用saveBatch逐条插入,数据库要执行1000次INSERT语句——这效率,简直能急死人!MP的批量操作功能,能让你用一条SQL搞定批量插入/更新,性能直接翻倍!

MP方案:saveBatch + 自定义批量SQL

MP默认提供了saveBatch(批量插入)和updateBatchById(批量更新)方法,但默认实现是逐条SQL(适合小批量)。如果是10万条数据,必须用自定义批量SQL优化!

1. 批量插入:默认实现VS自定义优化
  • 默认实现(小批量适用):

    List<User> userList = ...; // 100条数据
    boolean success = userService.saveBatch(userList); // 执行100条INSERT
    
  • 自定义批量插入(大批量适用):
    通过SqlInjector自定义一个批量插入的Mapper方法,用MySQL的INSERT INTO ... VALUES (),(),()语法,一条SQL插入所有数据。

    步骤1:自定义Mapper接口

    public interface UserMapper extends BaseMapper<User> {// 自定义批量插入方法(参数是List<User>)void batchInsert(@Param("list") List<User> list);
    }
    

    步骤2:编写XML SQL

    <!-- UserMapper.xml -->
    <insert id="batchInsert">INSERT INTO sys_user (username, create_time, update_time, is_deleted)VALUES <foreach collection="list" item="item" separator=",">(#{item.username}, #{item.createTime}, #{item.updateTime}, #{item.isDeleted})</foreach>
    </insert>
    

    步骤3:调用自定义方法

    userMapper.batchInsert(userList); // 一条SQL搞定1000条插入!
    
2. 批量更新:推荐用UpdateWrapper + updateBatchById

MP 3.5.0+ 支持更高效的批量更新方式,通过LambdaUpdateWrapper动态拼接条件,避免硬编码字段。

示例代码:

// 批量更新:将id为1、2、3的用户状态改为0(禁用)
List<Long> ids = Arrays.asList(1L, 2L, 3L);
boolean success = userService.update(new User().setStatus(0), new LambdaUpdateWrapper<User>().in(User::getId, ids)
);

MP会自动生成类似下面的SQL:

UPDATE sys_user 
SET status=0 
WHERE id IN (1, 2, 3)

⚠️ 避坑指南

  • 数据库连接池配置:大批量操作时,数据库连接池的maxActive(最大连接数)和maxWait(最大等待时间)要调大,避免连接不够用。
  • 分批处理:如果数据量超过1万条,建议分批处理(每批500-1000条),防止内存溢出。
  • 事务控制:批量操作一定要加@Transactional,避免部分成功部分失败导致数据不一致。

总结:MP功能增强,让开发效率翻倍!

今天我们深度解锁了MyBatis Plus的四大增强功能:

  • 自动填充:解放双手,告别手写时间戳和用户ID;
  • 逻辑删除:数据“软删除”,安全可追溯;
  • 乐观锁:高并发场景下的防覆盖神器;
  • 批量操作:从逐条SQL到一条SQL,性能飙升。

这些功能覆盖了开发中最常见的“重复劳动”和“性能瓶颈”,熟练掌握后,你的代码更简洁、更优雅!

最后提醒:实际项目中,要根据业务场景选择合适的方案。比如逻辑删除适合需要保留历史数据的场景,而批量操作适合数据导入/导出等批量任务。


文章转载自:
http://chefdoeuvre.zekgq.cn
http://astrogeology.zekgq.cn
http://antaeus.zekgq.cn
http://aeromancy.zekgq.cn
http://chapbook.zekgq.cn
http://benevolent.zekgq.cn
http://bareness.zekgq.cn
http://aerobatic.zekgq.cn
http://cathode.zekgq.cn
http://brook.zekgq.cn
http://cardamine.zekgq.cn
http://anemophilous.zekgq.cn
http://calamary.zekgq.cn
http://bumpity.zekgq.cn
http://bookselling.zekgq.cn
http://amylopsin.zekgq.cn
http://bunkmate.zekgq.cn
http://airdrome.zekgq.cn
http://capeesh.zekgq.cn
http://bluebottle.zekgq.cn
http://castice.zekgq.cn
http://benthograph.zekgq.cn
http://byzantine.zekgq.cn
http://catchment.zekgq.cn
http://baseball.zekgq.cn
http://aequorin.zekgq.cn
http://anaesthetics.zekgq.cn
http://ceremonialism.zekgq.cn
http://cacumen.zekgq.cn
http://californicate.zekgq.cn
http://www.dtcms.com/a/281305.html

相关文章:

  • 16、鸿蒙Harmony Next开发:组件扩展
  • KeilMDK5如何生成.bin文件
  • 项目进度跨地域团队协作困难,如何统一进度安排
  • PHP语法高级篇(三):Cookie与会话
  • Redis中的红锁
  • ADC采集、缓存
  • Axios 完整功能介绍和完整示例演示
  • 映美打印机-URL页面打印
  • Spring MVC 执行流程详解:一次请求经历了什么?
  • 微信小程序:在ios中border边框显示不全
  • XCTF-repeater三链破盾:PIE泄露+ROP桥接+Shellcode执行的艺术
  • PyTorch 数据加载实战:从 CSV 到图像的全流程解析
  • 股指期货主连和次主连的区别是什么?
  • 游戏加速器核心技术:动态超发
  • Linux 文件系统实现层详解:原理、结构与驱动衔接
  • 人类气道黏膜下腺类器官:解析呼吸炎症与感染的新平台
  • Sharding-JDBC 分布式事务实战指南:XA/Seata 方案解析(三)
  • (3)从零开发 Chrome 插件:网页图片的批量下载
  • Google EMM是什么?
  • Git Idea 冲突解决
  • GitHub Pages无法访问以点号.开头的目录
  • 【实时Linux实战系列】实时数据流的网络传输
  • 百度移动开发面经合集
  • 【matlab】三维路面谱生成代码
  • Altium Designer 25 安装与配置完整教程
  • 【高并发服务器】多路复用的总结 eventfd timerfd
  • 2.3 数组与字符串
  • Flutter 股票图实现分析与解决方案
  • 深入理解高性能字节池 bytebufferpool
  • 1.easypan-登录注册