很多小伙伴开发时都会遇到一堆问题复杂多条件模糊查询怎么写并发更新数据错乱怎么办逻辑删除后统计数据不准批量新增效率低自定义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 ListUser getUserList(String username, Integer age, String email){LambdaQueryWrapperUser 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代码干净又安全拓展排序、去重、指定字段查询LambdaQueryWrapperUser 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 IServiceUser {}// Service实现类Servicepublic class UserServiceImpl extends ServiceImplUserMapper, User implements UserService {}2. 高效批量新增/修改Autowiredprivate UserService userService;// 批量新增Testvoid batchSave(){ListUser userList new ArrayList();// 批量封装数据...userService.saveBatch(userList);}// 批量更新根据IDTestvoid batchUpdate(){ListUser userList new ArrayList();userService.updateBatchById(userList);}小细节批量方法支持设置批次大小saveBatch(list, 1000)适配大数据量场景避免一次性数据过多报错。四、乐观锁解决并发更新数据丢失问题生产高频Bug多人同时修改同一条数据后提交的数据会覆盖先提交的数据导致数据丢失。MP自带乐观锁插件无需手写锁逻辑完美解决并发更新问题。1. 数据库加版本字段新增字段version int default 1 comment 版本号2. 实体类添加注解Version // 乐观锁版本号注解private Integer version;3. 开启乐观锁插件Beanpublic 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自定义方法// 自定义多表查询方法ListUserVO getUserAndDeptList();2. 对应XML写原生SQL正常写联查、统计SQL即可MP的分页、日志、插件依旧生效完全兼容。六、生产避坑逻辑删除的隐形坑上篇讲过逻辑删除但很多人上线后出问题统计表数据不准、自定义SQL查不到已删除数据。坑点开启逻辑删除全局配置后MP会自动在所有查询拼接 where deleted0。解决方案需要查询全部数据包含已删除时手动关闭当前查询的逻辑删除// 忽略逻辑删除查询所有数据QueryWrapperUser wrapper new QueryWrapper().setSqlSelect(*);wrapper.last(limit 100);// 核心关闭本次查询逻辑删除userMapper.selectList(wrapper.ignoreLogicDelete());七、高级查询分页条件排序 组合实战给大家一套项目通用的后台列表分页模板直接复用public PageUser pageUser(Integer pageNum, Integer pageSize, String username, Integer age) {// 分页参数PageUser page new Page(pageNum, pageSize);// 动态条件LambdaQueryWrapperUser 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);}
MyBatis-Plus 进阶实战|告别只会CRUD!搞定企业级高频场景
发布时间:2026/5/29 2:10:39
很多小伙伴开发时都会遇到一堆问题复杂多条件模糊查询怎么写并发更新数据错乱怎么办逻辑删除后统计数据不准批量新增效率低自定义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 ListUser getUserList(String username, Integer age, String email){LambdaQueryWrapperUser 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代码干净又安全拓展排序、去重、指定字段查询LambdaQueryWrapperUser 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 IServiceUser {}// Service实现类Servicepublic class UserServiceImpl extends ServiceImplUserMapper, User implements UserService {}2. 高效批量新增/修改Autowiredprivate UserService userService;// 批量新增Testvoid batchSave(){ListUser userList new ArrayList();// 批量封装数据...userService.saveBatch(userList);}// 批量更新根据IDTestvoid batchUpdate(){ListUser userList new ArrayList();userService.updateBatchById(userList);}小细节批量方法支持设置批次大小saveBatch(list, 1000)适配大数据量场景避免一次性数据过多报错。四、乐观锁解决并发更新数据丢失问题生产高频Bug多人同时修改同一条数据后提交的数据会覆盖先提交的数据导致数据丢失。MP自带乐观锁插件无需手写锁逻辑完美解决并发更新问题。1. 数据库加版本字段新增字段version int default 1 comment 版本号2. 实体类添加注解Version // 乐观锁版本号注解private Integer version;3. 开启乐观锁插件Beanpublic 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自定义方法// 自定义多表查询方法ListUserVO getUserAndDeptList();2. 对应XML写原生SQL正常写联查、统计SQL即可MP的分页、日志、插件依旧生效完全兼容。六、生产避坑逻辑删除的隐形坑上篇讲过逻辑删除但很多人上线后出问题统计表数据不准、自定义SQL查不到已删除数据。坑点开启逻辑删除全局配置后MP会自动在所有查询拼接 where deleted0。解决方案需要查询全部数据包含已删除时手动关闭当前查询的逻辑删除// 忽略逻辑删除查询所有数据QueryWrapperUser wrapper new QueryWrapper().setSqlSelect(*);wrapper.last(limit 100);// 核心关闭本次查询逻辑删除userMapper.selectList(wrapper.ignoreLogicDelete());七、高级查询分页条件排序 组合实战给大家一套项目通用的后台列表分页模板直接复用public PageUser pageUser(Integer pageNum, Integer pageSize, String username, Integer age) {// 分页参数PageUser page new Page(pageNum, pageSize);// 动态条件LambdaQueryWrapperUser 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);}