03-MyBatis-Plus全功能实战:CRUD、条件构造器、分页、排序

发布时间:2026/10/23 10:15:17

03-MyBatis-Plus全功能实战:CRUD、条件构造器、分页、排序 MyBatis-Plus全功能实战CRUD、条件构造器、分页、排序黒漂技术佬 · 2026年7月前言前一篇文章我们啃完了 MyBatis 的原生招式Mapper XML、动态 SQL、#{}防注入、ResultMap 嵌套映射。学完这些你写个无人售货柜商品管理的后台已经不在话下。但是——你是不是也感觉到了单表 CRUD 这种事翻来覆去就是那几句 SQLSELECT*FROMt_deviceWHEREid?INSERTINTOt_device...UPDATEt_deviceSET...DELETEFROMt_deviceWHEREid?每张表都写一遍你烦不烦反正我烦了。于是就有了这篇文章的主角MyBatis-Plus简称 MP。MP 不是替代 MyBatis而是在 MyBatis 之上做增强。你之前学的 Mapper XML、动态 SQL 原封不动还能用MP 只是在此基础上帮你把重复劳动省掉。听起来是不是很诱人开搞。一、什么是 MyBatis-PlusMyBatis-Plus 是 MyBatis 的增强工具包内置通用 Mapper、通用 Service提供分页插件、条件构造器、代码生成器等能力目标是只做增强不做改变。简单翻译一下你原来怎么写 MyBatis 还怎么写但 MP 帮你吃掉了一大堆 boilerplate样板代码。举个例子不用 MP 的时候每种单表查询你都要在 XML 里写select有了 MP 之后MapperpublicinterfaceDeviceMapperextendsBaseMapperDevice{// 什么都不用写CRUD 自动有了}继承了BaseMapperT你直接就能调用deviceMapper.selectById(1L);// 查一条deviceMapper.selectList(null);// 查全部deviceMapper.insert(device);// 插入deviceMapper.updateById(device);// 更新deviceMapper.deleteById(1L);// 删除单表操作从此再也不写 XML爽不爽二、快速开始pom.xml 引入dependencygroupIdcom.baomidou/groupIdartifactIdmybatis-plus-boot-starter/artifactIdversion3.5.5/version/dependencyapplication.ymlmybatis-plus:mapper-locations:classpath*:/mapper/**/*.xml# XML 路径如果需要configuration:map-underscore-to-camel-case:true# 驼峰转换log-impl:org.apache.ibatis.logging.stdout.StdOutImpl# SQL 日志global-config:db-config:id-type:auto# 主键自增策略logic-delete-field:is_deleted# 逻辑删除字段logic-delete-value:1logic-not-delete-value:0配置项和原生 MyBatis 高度兼容只是换了个前缀mybatis-plus。三、实体类注解告诉 MP 你的表长什么样DataTableName(t_device)// 指定表名publicclassDevice{TableId(typeIdType.AUTO)// 主键自增privateLongid;TableField(device_sn)// 字段名和属性名不一致时指定privateStringdeviceSn;// 货柜设备编号TableField(device_name)privateStringdeviceName;// 设备名称如 A栋1F饮料柜privateStringlocation;// 安装位置TableField(valuestatus,existtrue)privateIntegerstatus;// 1在线 0离线TableField(fillFieldFill.INSERT)privateLocalDateTimecreateTime;TableField(fillFieldFill.INSERT_UPDATE)privateLocalDateTimeupdateTime;TableLogicprivateIntegerisDeleted;// 逻辑删除标记TableField(existfalse)// 数据库里没有这个字段privateStringstatusText;// 前端展示用在线/离线}逐一说明注解作用典型场景TableName指定实体对应的表名表名和类名不一致时必须加TableId标注主键字段指定生成策略IdType.AUTO自增IdType.ASSIGN_ID雪花算法TableField指定属性对应的列名数据库叫device_snJava 叫deviceSn时用TableField(exist false)标记非数据库字段临时计算结果、前端拼接文本等TableLogic逻辑删除标记delete 操作自动转为 updateTableField(fill ...)配合自动填充使用创建时间、更新时间自动注入TableId(type IdType.ASSIGN_ID)用的是雪花算法Snowflake生成的是一个 64 位的 Long 型 ID全局唯一。在分布式场景比如多台无人售货柜各自生成订单号下非常有用避免 ID 冲突。四、条件构造器没有 XML 的复杂查询这是 MyBatis-Plus 最核心也最强大的功能。如果只靠BaseMapper的默认方法只能做selectById、selectList这种简单操作。真正的业务查询——“查询所有在线的设备”、“按设备名称模糊搜索”、“按安装时间范围筛选”——需要条件构造器。MP 提供了两种条件构造器构造器特点推荐场景QueryWrapper字符串写列名拼错不报错简单查询、快速原型LambdaQueryWrapperLambda 表达式写列名编译期检查正式项目一律用这个4.1 LambdaQueryWrapper 常用 APIAutowiredprivateDeviceMapperdeviceMapper;// 1. 等值查询查询所有在线设备ListDeviceonlineDevicesdeviceMapper.selectList(newLambdaQueryWrapperDevice().eq(Device::getStatus,1));// 2. 模糊查询按设备名称搜索ListDevicedevicesdeviceMapper.selectList(newLambdaQueryWrapperDevice().like(Device::getDeviceName,饮料柜));// 3. 范围查询查找某个时间段内安装的设备ListDevicerecentDevicesdeviceMapper.selectList(newLambdaQueryWrapperDevice().between(Device::getCreateTime,LocalDateTime.of(2026,1,1,0,0),LocalDateTime.now()));// 4. IN 查询查多个指定设备ListDeviceselecteddeviceMapper.selectList(newLambdaQueryWrapperDevice().in(Device::getId,1L,3L,5L,7L));// 5. 组合条件在线 位置在北京 按创建时间倒序ListDeviceresultdeviceMapper.selectList(newLambdaQueryWrapperDevice().eq(Device::getStatus,1).likeRight(Device::getLocation,北京).orderByDesc(Device::getCreateTime).last(LIMIT 10)// 拼原生 SQL 片段);4.2 常用条件方法速查表方法含义SQL 等价eq等于column valuene不等于column valuegt / ge大于 / 大于等于column value/column valuelt / le小于 / 小于等于column value/column valuelike全模糊匹配column LIKE %value%likeLeft左模糊column LIKE %valuelikeRight右模糊column LIKE value%between范围column BETWEEN v1 AND v2in包含column IN (v1, v2, ...)isNull / isNotNull空值判断column IS NULLorderByAsc / orderByDesc排序ORDER BY column ASC/DESC用LambdaQueryWrapper有个巨大的好处你如果写Device::getDeivceName注意我故意写错了IDE 直接标红编译报错。而用QueryWrapper写字符串deivce_name拼错了要到运行时才发现——运维同学半夜又要爬起来。五、分页插件告别手算 SQL Limit业务中分页是刚需。手写分页 SQL 不复杂但每次都拼LIMIT offset, size着实无聊。而且除了数据还要查总条数两条 SQL 来回写。MP 的分页插件一步到位ConfigurationpublicclassMybatisPlusConfig{BeanpublicMybatisPlusInterceptormybatisPlusInterceptor(){MybatisPlusInterceptorinterceptornewMybatisPlusInterceptor();// 分页插件interceptor.addInnerInterceptor(newPaginationInnerInterceptor(DbType.MYSQL));returninterceptor;}}然后业务代码// 构建分页对象第1页每页10条PageDevicepagenewPage(1,10);// 加上条件LambdaQueryWrapperDevicewrappernewLambdaQueryWrapperDevice().eq(Device::getStatus,1)// 只要在线设备.orderByDesc(Device::getUpdateTime);// 执行分页查询PageDeviceresultdeviceMapper.selectPage(page,wrapper);System.out.println(当前页result.getCurrent());System.out.println(总页数result.getPages());System.out.println(总记录result.getTotal());System.out.println(数据列表result.getRecords());一条selectPage调用内部自动执行两条 SQLSELECT COUNT(*) FROM t_device WHERE status 1→ 查总数SELECT * FROM t_device WHERE status 1 ORDER BY update_time DESC LIMIT 0, 10→ 查分页数据你再也不用自己写两个查询方法了。六、自动填充createTime/updateTime 不用手动设后台管理系统里几乎所有表都有create_time和update_time每次新增或更新都要手动setCreateTime(now)麻烦且容易忘。MP 的自动填充功能专门解决这个问题ComponentpublicclassMyMetaObjectHandlerimplementsMetaObjectHandler{OverridepublicvoidinsertFill(MetaObjectmetaObject){this.strictInsertFill(metaObject,createTime,LocalDateTime.class,LocalDateTime.now());this.strictInsertFill(metaObject,updateTime,LocalDateTime.class,LocalDateTime.now());}OverridepublicvoidupdateFill(MetaObjectmetaObject){this.strictUpdateFill(metaObject,updateTime,LocalDateTime.class,LocalDateTime.now());}}配合实体类上的注解TableField(fillFieldFill.INSERT)privateLocalDateTimecreateTime;TableField(fillFieldFill.INSERT_UPDATE)privateLocalDateTimeupdateTime;之后你 insert/update 实体对象时根本不用管时间字段MP 在 SQL 执行前自动帮你填上。妈妈再也不用担心我看到createTime null的报错了。七、逻辑删除假删除真安全项目里几乎没有人敢直接DELETE FROM t_device WHERE id 1——数据丢了找不回来审计也过不了。常规操作是加一个is_deleted字段删的时候设1查的时候带WHERE is_deleted 0。但每个查询都要记得加这个条件烦不烦MP 帮你做了TableLogicprivateIntegerisDeleted;全局配置mybatis-plus:global-config:db-config:logic-delete-field:is_deletedlogic-delete-value:1# 已删除logic-not-delete-value:0# 未删除效果deviceMapper.deleteById(1L)→ 实际执行UPDATE t_device SET is_deleted 1 WHERE id 1所有select操作自动追加WHERE is_deleted 0需要查已删除数据用deviceMapper.selectList(...)加上.eq(Device::getIsDeleted, 1)即可注意逻辑删除和唯一索引有冲突。比如设备编号device_sn设了唯一键逻辑删除后新建一条相同编号的记录会失败。这时要么不用唯一索引要么把is_deleted也纳入唯一索引。八、乐观锁并发场景的数据安全保障无人售货柜的场景管理员 A 看到某设备信息在编辑管理员 B 也在同一时刻编辑同一台设备。A 点保存B 也点保存——后保存的把先保存的覆盖了这就是典型的丢失更新问题。乐观锁的解决思路在表中加一个version字段每次更新时带上版本号比对版本号匹配才更新同时版本号 1。VersionprivateIntegerversion;需要配置一个拦截器BeanpublicMybatisPlusInterceptormybatisPlusInterceptor(){MybatisPlusInterceptorinterceptornewMybatisPlusInterceptor();interceptor.addInnerInterceptor(newOptimisticLockerInnerInterceptor());returninterceptor;}实际执行的 SQL 会变成UPDATEt_deviceSETdevice_name新名称,version3WHEREid1ANDversion2如果version已经被别人更新成了 3这条 SQL 影响行数为 0MP 会抛出OptimisticLockException上游捕获后提示数据已被他人修改请刷新重试——完美。九、完整实战无人售货柜设备管理把上面的功能串成一份完整的设备管理代码。Service 层publicinterfaceDeviceServiceextendsIServiceDevice{PageDevicepageSearch(Stringkeyword,Integerstatus,intpageNum,intpageSize);booleanonline(Longid);// 设备上线booleanoffline(Longid);// 设备离线}ServicepublicclassDeviceServiceImplextendsServiceImplDeviceMapper,DeviceimplementsDeviceService{OverridepublicPageDevicepageSearch(Stringkeyword,Integerstatus,intpageNum,intpageSize){LambdaQueryWrapperDevicewrappernewLambdaQueryWrapper();if(StringUtils.hasText(keyword)){wrapper.and(w-w.like(Device::getDeviceName,keyword).or().like(Device::getDeviceSn,keyword));}if(status!null){wrapper.eq(Device::getStatus,status);}wrapper.orderByDesc(Device::getUpdateTime);returnthis.page(newPage(pageNum,pageSize),wrapper);}Overridepublicbooleanonline(Longid){DevicedevicenewDevice();device.setId(id);device.setStatus(1);returnthis.updateById(device);}Overridepublicbooleanoffline(Longid){DevicedevicenewDevice();device.setId(id);device.setStatus(0);returnthis.updateById(device);}}Controller 层RestControllerRequestMapping(/api/device)publicclassDeviceController{AutowiredprivateDeviceServicedeviceService;GetMapping(/page)publicResultPageDevicepage(RequestParam(requiredfalse)Stringkeyword,RequestParam(requiredfalse)Integerstatus,RequestParam(defaultValue1)intpageNum,RequestParam(defaultValue10)intpageSize){returnResult.success(deviceService.pageSearch(keyword,status,pageNum,pageSize));}PostMapping(/{id}/online)publicResultVoidonline(PathVariableLongid){returndeviceService.online(id)?Result.success():Result.fail(操作失败);}PostMapping(/{id}/offline)publicResultVoidoffline(PathVariableLongid){returndeviceService.offline(id)?Result.success():Result.fail(操作失败);}}ServiceImplT是 MP 提供的通用 Service 实现继承了它就有了全套save、remove、update、page、list等方法。你只需要在它上面补几行自定义逻辑就行。总结MyBatis-Plus 的正确使用姿势总结Mapper 继承BaseMapperT单表 CRUD 自由了不用写一行 SQL条件查询用LambdaQueryWrapper编译期保证字段名正确杜绝 runtime 翻车分页用PaginationInnerInterceptor一行selectPage搞定连总数都帮你算了自动填充用MetaObjectHandler时间字段从此无人值守逻辑删除 乐观锁数据安全的两大护法简单注解就搞定MP 不是替代 MyBatis而是让它更好用。复杂联表查询、存储过程这些XML 照样上但 80% 的单表操作场景MP 让你把开发效率拉满。
延伸阅读

更多相关文章

2026/10/21 22:51:45

常见电路设计——(1)超级电容充电电路

目录 整体电路架构 一、恒流主回路器件(CC 阶段核心) 1. R500、R501(恒流采样电阻) 2. T501(主功率调整管) 3. T500(电流检测 / 负反馈管) 4. R502(基极限流电阻) 二、恒压控制环路器件(CV 阶段核心) 1. U500(TL431,精密基准源) 2. T502(电压环误差放…

2026/10/23 10:15:00

VS2017 Win32贪吃蛇:Windows GUI开发最小闭环实践

简介:本资源是一份基于Visual Studio 2017平台开发的C语言贪吃蛇游戏完整工程,面向计算机专业本科生及数据结构初学者,用于课程设计、大作业实践与GUI编程入门。项目实现方向键控制、多类型果实、加速/减速机制、三界面系统(开始/…

2026/10/23 10:15:00

Python超市管理系统:tkinter+SQLite轻量级实战项目

简介:这是一套基于Python开发的轻量级超市管理系统源码,面向Python初学者与课程设计实践者,聚焦日常运营中的员工管理、客户信息维护及登录权限控制等核心业务场景。资源共13个文件,包含4个核心Python模块(如login.py实…

2026/10/23 10:15:00

Avalonia中带返回值的异步命令实现方案

1. 项目概述:为什么一个“带返回值的异步命令”值得单独写一篇深度实践笔记?在 Avalonia 这个跨平台 .NET UI 框架的实际开发中,我几乎每天都会遇到同一个高频痛点:用户点击按钮后要发起网络请求、读取本地文件、调用加密服务——…

2026/10/23 10:15:00

柔性车间调度中遗传算法的约束编译与解空间曲率适配

简介:本资源是一份面向智能制造与运筹优化方向的本科高年级学生、研究生及工业算法工程师的柔性作业车间调度(FJSP)实战代码,聚焦遗传算法(GA)在复杂调度场景中的建模与求解。压缩包仅含1个核心Matlab源文件…

2026/10/23 10:15:00

RAII:C++资源管理的核心范式与工程实践

1. RAII 是什么?一句话说清它为什么是 C 的“呼吸感”设计RAII,全称 Resource Acquisition Is Initialization(资源获取即初始化),不是某个库、不是语法糖、更不是编译器黑魔法——它是 C 语言哲学层面的一条铁律&…

2026/10/23 10:09:56

JSP隐式对象与四大作用域详解:从原理到实战避坑

1. 从一次“数据莫名消失”的调试说起做 JSP 开发的人,几乎都经历过这样的场景:明明在页面里把数据塞进去了,翻到下一个页面却怎么也取不出来;或者同一个变量,在 A 页面是值,在 B 页面变成了 null&#xff…

2026/10/22 11:29:28

金融AI落地指南:从调研报告到技术选型与工程实践

简介:这份PDF报告聚焦2025年金融服务业中国AI现状与趋势,面向金融科技从业者、AI战略规划人员及关注行业数字化转型的研究者,帮助读者把握生成式AI在银行、支付、交易等场景中的落地路径与投资方向。资源包内含1个PDF文件,大小约1…

2026/10/23 0:02:26

Verilog实现FPGA密码锁:矩阵键盘与状态机设计实战

说实话,这几年接触了不少想入门FPGA的朋友,如果你问哪个项目能一口气把Verilog的核心基本功串起来,我通常会推荐电子密码锁。这个项目看着不起眼,却把矩阵键盘扫描、按键消抖、状态机设计、动态显示刷新、外设控制这些数字逻辑里的…

2026/10/23 0:02:26

数据服务监控运维实战:从指标体系到告警治理与故障排查

周末凌晨两点,手机震了。第一反应是“数据服务挂了”。打开电脑看了下,果然,某张核心报表的数据产出时间已经超过SLA两小时,下游三个业务方连着发来问询。这种场景,做过大数据运维的人都不会陌生。数据服务的监控与运维…

2026/10/23 0:02:26

FreeRTOS任务通知实战详解:内核原理、API与避坑指南

裸机编程切到RTOS之后,任务之间的通信和同步永远是躲不开的坎。前面聊过队列、信号量、互斥锁、事件组,这次把FreeRTOS任务通知(Task Notification)单独拎出来讲透。原因很简单:任务通知是FreeRTOS里最容易用错、也最容…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑