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

MyBatis-Plus 进阶实战|告别只会CRUD!搞定企业级高频场景

很多小伙伴开发时都会遇到一堆问题:

复杂多条件模糊查询怎么写?并发更新数据错乱怎么办?逻辑删除后统计数据不准?批量新增效率低?自定义SQL怎么和MP混用?

只会基础CRUD,只能应付简单demo。真正的企业项目,全靠MP高阶功能撑场面。

今天这篇进阶干货,专门解决工作中90%的MP实战难题,看完直接适配生产环境开发!


一、先划重点:MP进阶核心思维

很多人用MP有个误区:把MP当成“只会生成SQL的工具”。

实际上企业级MP核心用法就两点:

1)简单业务:全程用MP条件构造器,零SQL开发

2)复杂业务/多表联查:MP与原生SQL混用,灵活不冲突

下面全是生产级高频用法,直接复制就能用!

二、LambdaQueryWrapper 高阶查询(避坑版)

基础的等值、范围查询大家都会,工作中最常用、最容易出错的是动态多条件拼接。

场景:后台列表查询,用户名、年龄、邮箱都是非必传参数,传了才查询,不传不生效。

1)新手错误写法

无脑拼接条件,容易出现空字符串、null导致查询数据异常:

// 极易出Bug!空参数也会拼接条件

wrapper.like(User::getUsername, username);

wrapper.gt(User::getAge, age);

2)进阶优雅写法(带条件判断)

MP自带 condition 参数,满足条件才拼接SQL,超级简洁:

public List<User> getUserList(String username, Integer age, String email){

LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();

// 仅当用户名不为空时,执行模糊查询

wrapper.like(StringUtils.hasText(username), User::getUsername, username)

// 仅当年龄不为null时,执行大于查询

.gt(age != null, User::getAge, age)

// 仅当邮箱不为空时,精准匹配

.eq(StringUtils.hasText(email), User::getEmail, email);

return userMapper.selectList(wrapper);

}

核心技巧:所有条件构造方法的第一个参数,都可以加布尔判断,彻底干掉一堆 if-else,代码干净又安全!

拓展:排序、去重、指定字段查询

LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();

// 只查询指定字段,避免查询全部字段浪费性能

wrapper.select(User::getId, User::getUsername, User::getAge);

// 年龄倒序,创建时间正序

wrapper.orderByDesc(User::getAge)

.orderByAsc(User::getCreateTime);

// 去重查询

wrapper.distinct(true);

三、批量操作:告别for循环单条保存

新手开发批量新增/修改,总喜欢用 for循环 + insert/update,数据库频繁交互,性能极差。

MP内置ServiceImpl,自带高效批量方法,底层是批量预处理SQL,性能碾压循环操作。

1. 先改造Service层(进阶必备)

基础Mapper只适合单条操作,批量操作必须用IService:

// Service接口

public interface UserService extends IService<User> {}

// Service实现类

@Service

public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {}

2. 高效批量新增/修改

@Autowired

private UserService userService;

// 批量新增

@Test

void batchSave(){

List<User> userList = new ArrayList<>();

// 批量封装数据...

userService.saveBatch(userList);

}

// 批量更新(根据ID)

@Test

void batchUpdate(){

List<User> userList = new ArrayList<>();

userService.updateBatchById(userList);

}

小细节:批量方法支持设置批次大小,saveBatch(list, 1000),适配大数据量场景,避免一次性数据过多报错。

四、乐观锁:解决并发更新数据丢失问题

生产高频Bug:多人同时修改同一条数据,后提交的数据会覆盖先提交的数据,导致数据丢失。

MP自带乐观锁插件,无需手写锁逻辑,完美解决并发更新问题。

1. 数据库加版本字段

新增字段:version int default 1 comment '版本号'

2. 实体类添加注解

@Version // 乐观锁版本号注解

private Integer version;

3. 开启乐观锁插件

@Bean

public MybatisPlusInterceptor mybatisPlusInterceptor(){

MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();

interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));

// 添加乐观锁插件

interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());

return interceptor;

}

实现原理:每次更新会校验版本号,更新成功版本号+1;如果版本号不匹配(被他人修改过),更新失败,避免数据覆盖。

五、MP + 自定义SQL 完美混用

MP不是万能的,多表联查、复杂统计、子查询,还是原生SQL最顺手。

重点:MP可以和原生MyBatis完全混用,互不冲突!

1. Mapper自定义方法

// 自定义多表查询方法

List<UserVO> getUserAndDeptList();

2. 对应XML写原生SQL

正常写联查、统计SQL即可,MP的分页、日志、插件依旧生效,完全兼容。

六、生产避坑:逻辑删除的隐形坑

上篇讲过逻辑删除,但很多人上线后出问题:统计表数据不准、自定义SQL查不到已删除数据。

坑点:

开启逻辑删除全局配置后,MP会自动在所有查询拼接 where deleted=0。

解决方案:

需要查询全部数据(包含已删除)时,手动关闭当前查询的逻辑删除:

// 忽略逻辑删除,查询所有数据

QueryWrapper<User> wrapper = new QueryWrapper<>().setSqlSelect("*");

wrapper.last("limit 100");

// 核心:关闭本次查询逻辑删除

userMapper.selectList(wrapper.ignoreLogicDelete());

七、高级查询:分页+条件+排序 组合实战

给大家一套项目通用的后台列表分页模板,直接复用:

public Page<User> pageUser(Integer pageNum, Integer pageSize, String username, Integer age) {

// 分页参数

Page<User> page = new Page<>(pageNum, pageSize);

// 动态条件

LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();

wrapper.like(StringUtils.hasText(username), User::getUsername, username)

.gt(age != null, User::getAge, age)

.orderByDesc(User::getCreateTime);

// 分页查询

return userMapper.selectPage(page, wrapper);

}

http://www.jsqmd.com/news/906852/

相关文章:

  • 基于Arduino与3D打印的BB-8球形机器人制作全攻略
  • Pythonio字节流与文本流
  • 徐州地铁旁高端写字楼
  • Cursor AI Pro破解工具:智能解锁神器,告别试用限制的终极解决方案
  • 避坑指南:Unity ShaderGraph做刮刮乐效果,为什么你的笔刷边缘有锯齿?
  • 10分钟玩转LLM API调用+Prompt设计,零基础也能快速落地AI应用
  • 告别卡顿!在AMD笔记本(如R7 6800H)上用VMware流畅运行macOS开发环境的完整配置流程
  • 英语句法分析
  • 2026年科华UPS电源采购,北京哪家靠谱?
  • 食品包装AI质检时代来了,标签审核效率提升千倍
  • qmcdump:如何用3步解锁QQ音乐加密文件实现跨平台播放自由
  • 终极RPG Maker解密工具:3步轻松提取加密游戏资源
  • 用8050三极管和FR107二极管,我复刻了一个简易ZVS振荡电路(附完整电路图)
  • 别再只盯着折射率了!ZEMAX热分析中,空气间隔和机械半口径(MCSD)才是关键
  • 保姆级教程:在Ubuntu 20.04上用GStreamer 1.16.2源码编译并启动你的第一个RTSP服务器
  • 订单超时库存不释放?手把手教你用RabbitMQ死信队列实现自动解锁(SpringBoot实战)
  • Unity InputSystem虚拟摇杆实战:从基础配置到三种高级模式(固定/跟随/灵活)
  • 用Python玩转强化学习:从‘赌徒问题’实战理解MDP的策略迭代与价值迭代
  • 别再被Finder骗了!Mac里多出来的那个‘Macintosh HD’到底是什么?APFS卷组与firmlink机制全解析
  • 保姆级教程:在Ubuntu Server 22.04上搞定图形桌面和VNC远程连接(含RealVNC账号注册避坑)
  • 3D打印热床附着力与高温PI胶带应用技术指南
  • 别再只盯着TXOUTCLK了!手把手教你用FPGA的RXOUTCLK(线路恢复时钟)驱动RXUSRCLK
  • 深入UGUI底层:手把手教你用OnPopulateMesh和顶点偏移,实现Image的任意2D变形
  • 一文读懂AI人工智能:从概念到范式,小白也能秒懂
  • Keil µVision编译错误信息缺失的McAfee杀毒软件解决方案
  • 避坑指南:macOS重装/降级时,磁盘工具抹掉选项怎么选?APFS还是Mac OS扩展?
  • 别再乱改权限了!用微软官方AccessChk工具,5分钟排查Windows系统安全漏洞
  • 从‘平均主义’到‘精准加权’:手把手复现阿里DIN模型中的Attention Unit(附PyTorch代码)
  • 新型智慧城市 + 城市大数据应用完整解决方案(架构 + 平台建设 + 落地实践)
  • pdfClaw免登录在线PDF转Word