【MyBatis-Plus 进阶功能】开发中常用场景剖析
MyBatis-Plus(MP)除了封装常见的 CRUD 操作,还提供了一些高级功能,进一步简化复杂场景下的开发工作。本文将逐一讲解 逻辑删除、自动填充、多表关联查询的原理与使用方式,让你快速掌握这些技巧!
一、逻辑删除
逻辑删除是指在数据库中不直接删除记录,而是通过标记(如 is_deleted
字段)表示数据是否有效。
1. 原理与配置
逻辑删除是指在数据库中不直接删除记录,而是通过标记(如 is_deleted
字段)来表示数据是否有效,查询时会自动过滤掉已标记为“删除”的记录。
2. 配置步骤
-
在数据库表中添加逻辑删除字段(如
is_deleted
):ALTER TABLE user ADD COLUMN is_deleted TINYINT(1) DEFAULT 0 COMMENT '逻辑删除标记';
-
在实体类中添加逻辑删除注解:
@TableLogic private Integer isDeleted;
-
全局配置逻辑删除行为(
application.yml
):mybatis-plus:global-config:db-config:logic-delete-field: is_deletedlogic-delete-value: 1 # 逻辑已删除值logic-not-delete-value: 0 # 逻辑未删除值
3. 使用示例
① 插入记录前的数据库表数据
执行以下查询语句:
SELECT * FROM user;
假设初始数据为空:
id | name | is_deleted |
---|---|---|
② 插入新记录
User user = new User();
user.setName("Tom");
userMapper.insert(user);
执行后,生成的 SQL 为:
INSERT INTO user (name, is_deleted) VALUES ('Tom', 0);
③ 数据库表中新增记录
id | name | is_deleted |
---|---|---|
1 | Tom | 0 |
④ 执行逻辑删除操作
userMapper.deleteById(1);
生成的 SQL 为:
UPDATE user SET is_deleted = 1 WHERE id = 1;
⑤ 数据库表更新后的记录
id | name | is_deleted |
---|---|---|
1 | Tom | 1 |
⑥ 查询数据
默认情况下,逻辑删除记录不会出现在查询结果中:
List<User> users = userMapper.selectList(null);
生成的 SQL 为:
SELECT id, name FROM user WHERE is_deleted = 0;
查询结果:
id | name |
---|---|
若想查询所有记录(包括逻辑删除的记录),需要自定义 SQL:
@Select("SELECT * FROM user")
List<User> selectAllUsers();
查询结果:
id | name | is_deleted |
---|---|---|
1 | Tom | 1 |
通过示例可以看出,逻辑删除通过 自动更新 is_deleted
字段 和 动态添加查询条件 实现了逻辑上的“删除”操作,同时保留了数据的可追溯性。
4. 注意事项
- 逻辑删除仅在 MP 自动生成的 SQL 中生效,自定义 SQL 需手动添加条件。
- 查询时默认不包含已删除记录,若需查询已删除数据,需使用自定义 SQL。
二、自动填充
自动填充用于在插入或更新记录时,自动为某些字段赋值(如创建时间、更新时间)。
1. 原理与配置
实现自动填充的关键是使用 @TableField
的 fill
属性,并结合 MetaObjectHandler
接口完成填充逻辑。
自动填充的核心原理在于 MyBatis-Plus 提供的元对象处理器(MetaObjectHandler
)接口。通过实现这个接口,MP 能够在插入或更新数据时自动填充指定字段。这背后实际上是一种 拦截机制:在执行插入或更新操作时,MP 会检测是否有配置的填充字段,如果有,就调用实现类的逻辑为这些字段赋值。
以下我们详细来分析一下:
- 自动填充的触发时机
MP 在执行插入(INSERT
)或更新(UPDATE
)操作时,会检查实体类中是否有标注了@TableField(fill = ...)
的字段。如果存在这些字段,则会触发自动填充逻辑。 - 元对象处理器的作用
MP 使用MetaObjectHandler
接口实现了这个逻辑,通过重写其中的insertFill
和updateFill
方法,可以定义插入或更新时如何自动填充字段。 - 拦截机制
- MP 会在内部生成的 SQL 语句执行前,通过拦截器将指定字段的值动态插入到 SQL 语句中。
- 这一过程对开发者透明,减少了重复编写代码的成本。
- 配置生效
- MP 的核心配置会自动扫描所有实现了
MetaObjectHandler
的类,并在执行插入或更新操作时调用相关方法。 - 无需手动绑定,这也是为什么只需要实现类即可实现自动填充的原因。
- MP 的核心配置会自动扫描所有实现了
2. 配置步骤
-
在实体类中通过
@TableField(fill = ...)
指定需要填充的字段:@TableField(fill = FieldFill.INSERT) private LocalDateTime createTime;@TableField(fill = FieldFill.INSERT_UPDATE) private LocalDateTime updateTime;
FieldFill.INSERT
:仅在插入时填充。FieldFill.INSERT_UPDATE
:在插入和更新时都会填充。 -
实现
MetaObjectHandler
接口。只需创建一个类并实现MetaObjectHandler
,就会自动扫描并加载这个类的配置,无需额外声明。记得通过@Component
注解标注为组件,SpringBoot 才能够扫描到@Component public class MyMetaObjectHandler implements MetaObjectHandler {@Overridepublic void insertFill(MetaObject metaObject) {this.strictInsertFill(metaObject, "createTime", LocalDateTime::now, LocalDateTime.class);this.strictInsertFill(metaObject, "updateTime", LocalDateTime::now, LocalDateTime.class);}@Overridepublic void updateFill(MetaObject metaObject) {this.strictUpdateFill(metaObject, "updateTime", LocalDateTime::now, LocalDateTime.class);} }
strictInsertFill
和strictUpdateFill
是 MP 提供的方法,用于安全地为指定字段赋值。MP 会自动调用
insertFill
和updateFill
,而开发者无需显式调用。
3. 使用示例
① 插入数据
插入或更新时,无需手动设置 createTime
和 updateTime
:
User user = new User();
user.setName("Tom");
userMapper.insert(user);
执行后,自动填充的 SQL 为:
INSERT INTO user (name, create_time, update_time)
VALUES ('Tom', '2025-01-01 12:00:00', '2025-01-01 12:00:00');
② 更新数据
User user = userMapper.selectById(1);
user.setName("Jerry");
userMapper.updateById(user);
执行后,自动填充的 SQL 为:
UPDATE user
SET name = 'Jerry', update_time = '2025-01-01 12:05:00'
WHERE id = 1;
三、多表关联查询
虽然 MyBatis-Plus 不直接支持多表联查,但我们可以通过以下方式实现:
1. 数据库表设计
假设我们有以下两张表:
用户表(user
)
字段名 | 类型 | 描述 |
---|---|---|
id | INT | 主键 |
name | VARCHAR(50) | 用户名 |
VARCHAR(50) | 邮箱 |
订单表(order
)
字段名 | 类型 | 描述 |
---|---|---|
id | INT | 主键 |
user_id | INT | 用户 ID |
order_time | DATETIME | 下单时间 |
amount | DECIMAL(10, 2) | 订单金额 |
2. 实体类定义
用户实体类(User
)
@Data
public class User {private Integer id;private String name;private String email;// 关联的订单集合private List<Order> orders;
}
订单实体类(Order
)
@Data
public class Order {private Integer id;private Integer userId;private LocalDateTime orderTime;private BigDecimal amount;
}
3. XML 配置
自定义 SQL 和返回结果映射
通过 MyBatis 的 XML 配置实现多表联查:
UserMapper.xml
<mapper namespace="com.example.mapper.UserMapper"><!-- 多表联查,查询用户及其订单 --><select id="getUserWithOrders" resultMap="userWithOrdersMap">SELECT u.id AS userId,u.name AS userName,u.email AS userEmail,o.id AS orderId,o.order_time AS orderTime,o.amount AS orderAmountFROM user uLEFT JOIN order o ON u.id = o.user_idWHERE u.id = #{userId}</select><!-- 结果映射 --><resultMap id="userWithOrdersMap" type="com.example.entity.User"><id property="id" column="userId" /><result property="name" column="userName" /><result property="email" column="userEmail" /><collection property="orders" ofType="com.example.entity.Order"><id property="id" column="orderId" /><result property="orderTime" column="orderTime" /><result property="amount" column="orderAmount" /></collection></resultMap></mapper>
4. Mapper 接口
public interface UserMapper extends BaseMapper<User> {User getUserWithOrders(@Param("userId") Integer userId);
}
5. 测试联查
Service 层调用
@Autowired
private UserMapper userMapper;public User getUserWithOrders(Integer userId) {return userMapper.getUserWithOrders(userId);
}
测试用例
@Test
public void testGetUserWithOrders() {User user = userMapper.getUserWithOrders(1);System.out.println("用户信息:" + user.getName());System.out.println("订单信息:");user.getOrders().forEach(order -> {System.out.println("订单 ID:" + order.getId());System.out.println("订单金额:" + order.getAmount());});
}
6. 查询结果
假设 user
表中有以下记录:
id | name | |
---|---|---|
1 | 张三 | zhangsan@mail.com |
order
表中有以下记录:
id | user_id | order_time | amount |
---|---|---|---|
1 | 1 | 2025-01-01 12:00:00 | 100.00 |
2 | 1 | 2025-01-01 15:00:00 | 200.00 |
运行测试后输出:
用户信息:Alice
订单信息:
订单 ID:1
订单金额:100.00
订单 ID:2
订单金额:200.00
相关文章:
【MyBatis-Plus 进阶功能】开发中常用场景剖析
MyBatis-Plus(MP)除了封装常见的 CRUD 操作,还提供了一些高级功能,进一步简化复杂场景下的开发工作。本文将逐一讲解 逻辑删除、自动填充、多表关联查询的原理与使用方式,让你快速掌握这些技巧! 一、逻辑删…...

【C++/控制台】2048小游戏
源代码: #include <iostream> #include <windows.h> #include <stdio.h> #include <math.h> #include <stdlib.h> #include <conio.h> #include <time.h>// #define KEY_DOWN(VK_NONAME) ((GetAsyncKeyState(VK_NONAME)…...
Linux 中 top 命令的使用与实例解读
目录 Linux 中 top 命令的使用与实例解读一、top 命令参数二、输出字段含义(一)系统信息(二)任务信息(三)CPU 信息(四)内存信息 三、实例解读系统信息任务信息CPU信息内存信息进程列…...
C++ STL 中的 `unordered_map` 和 `unordered_set` 总结
1. unordered_map unordered_map 是一个基于哈希表实现的容器,存储键值对(key-value),每个键必须唯一,可以快速插入、删除、查找。 基本特性 存储结构:键值对 (key-value)。键唯一性:每个键在…...

机器学习基础-概率图模型
(一阶)马尔科夫模型的基本概念 状态、状态转换概率、初始概率 状态转移矩阵的基本概念 隐马尔可夫模型(HMM)的基本概念 条件随机场(CRF)的基本概念 实际应用中的马尔科夫性 自然语言处理: 在词性…...

【MySQL】九、表的内外连接
文章目录 前言Ⅰ. 内连接案例:显示SMITH的名字和部门名称 Ⅱ. 外连接1、左外连接案例:查询所有学生的成绩,如果这个学生没有成绩,也要将学生的个人信息显示出来 2、右外连接案例:对stu表和exam表联合查询,把…...
芯片详细讲解,从而区分CPU、MPU、DSP、GPU、FPGA、MCU、SOC、ECU
目录 芯片的概念结构 芯片的派系划分 通用芯片(CPU,MPU,GPU,DSP) 定制芯片(FPGA,ASIC) 芯片之上的集成(MCU,SOC,ECU) 软硬件的匹…...
halcon三维点云数据处理(十)locate_cylinder_3d
目录 一、locate_cylinder_3d例程代码二、gen_binocular_rectification_map函数三、binocular_disparity函数四、自定义函数select_best_candidates五、自定义函数remove_shadowed_regions 一、locate_cylinder_3d例程代码 1、读取或者创建3D形状模型, 2、根据双目…...
vue(2,3), react (16及以上)开发者工具资源
在前端开发的广阔领域中,Vue.js 和 React.js 作为两大主流框架,各自拥有庞大的用户群体和丰富的生态系统。为了帮助开发者更高效地进行调试和开发,Vue Devtools 和 React 开发者工具应运而生,成为这两个框架不可或缺的辅助工具。本…...

2025年华为OD上机考试真题(Java)——整数对最小和
题目: 给定两个整数数组array1、array2,数组元素按升序排列。假设从array1、array2中分别取出一个元素可构成一对元素,现在需要取出k对元素,并对取出的所有元素求和,计算和的最小值。 注意:两对元素如果对应…...

进程间通信——网络通信——UDP
进程间通信(分类):网络通信、无名管道、有名管道、信号、消息队列、共享内存、信号量集 OSI七层模型:(理论模型) 应用层 : 要传输的数据信息,如文件传输,电子邮件等 表示层 : 数…...

【我的 PWN 学习手札】IO_FILE 之 FSOP
FSOP:File Stream Oriented Programming 通过劫持 _IO_list_all 指向伪造的 _IO_FILE_plus,进而调用fake IO_FILE 结构体对象中被伪造的vtable指向的恶意函数。 目录 前言 一、glibc-exit函数浅析 二、FSOP 三、Largebin attack FSOP (…...

新兴的开源 AI Agent 智能体全景技术栈
新兴的开源 AI Agent 智能体全景技术栈 LLMs:开源大模型嵌入模型:开源嵌入模型模型的访问和部署:Ollama数据存储和检索:PostgreSQL, pgvector 和 pgai后端:FastAPI前端:NextJS缺失的一环:评估和…...

统计学习方法(第二版) 概率分布学习
本文主要介绍机器学习的概率分布,帮助后续的理解。 定义直接从书上搬的想自己写,但没有定义准确,还浪费事件,作为个人笔记,遇到速查。 目录 一、二点分布(0-1分布、伯努利分布) 二、二项分布…...

淺談Cocos2djs逆向
前言 簡單聊一下cocos2djs手遊的逆向,有任何相關想法歡迎和我討論^^ 一些概念 列出一些個人認為比較有用的概念: Cocos遊戲的兩大開發工具分別是CocosCreator和CocosStudio,區別是前者是cocos2djs專用的開發工具,後者則是coco…...

【ROS2】RViz2加载URDF模型文件
1、RViz2加载URDF模型文件 1)运行RViz2 rviz22)添加组件:RobotModel 3)选择通过文件添加 4)选择URDF文件,此时会报错,需要修改Fixed Frame为map即可 5)因为没有坐标转换,依然会报错,下面尝试解决 2、运行坐标转换节点 1)运行ROS节点:robot_state_publishe...

Unity导入特效,混合模式无效问题
检查spine导出设置与Unity导入设置是否一致 检查Blend Mode Materials是否勾选 检查是否使用导入时产生的对应混合模式的材质,混合模式不适用默认材质 这里选导入时生成的材质...

el-table自定义按钮控制扩展expand
需求:自定义按钮实现表格扩展内容的展开和收起,实现如下: 将type“expand”的表格列的宽度设置为width"1",让该操作列不展示出来,然后通过ref动态调用组件的内部方法toggleRowExpansion(row, row.expanded)控…...
opencv CV_TM_SQDIFF未定义标识符
opencv CV_TM_SQDIFF未定义标识符 opencv4部分命名发生变换,将CV_WINDOW_AUTOSIZE改为WINDOW_AUTOSIZE;CV_TM_SQDIFF_NORMED改为TM_SQDIFF_NORMED。...
2024acl论文体悟
总结分析归纳 模型架构与训练方法:一些论文关注于改进大语言模型的架构和训练方法,以提高其性能和效率。例如,“Quantized Side Tuning: Fast and Memory-Efficient Tuning of Quantized Large Language Models”提出了一种量化侧调优方法&a…...

基于距离变化能量开销动态调整的WSN低功耗拓扑控制开销算法matlab仿真
目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.算法仿真参数 5.算法理论概述 6.参考文献 7.完整程序 1.程序功能描述 通过动态调整节点通信的能量开销,平衡网络负载,延长WSN生命周期。具体通过建立基于距离的能量消耗模型&am…...

Appium+python自动化(十六)- ADB命令
简介 Android 调试桥(adb)是多种用途的工具,该工具可以帮助你你管理设备或模拟器 的状态。 adb ( Android Debug Bridge)是一个通用命令行工具,其允许您与模拟器实例或连接的 Android 设备进行通信。它可为各种设备操作提供便利,如安装和调试…...

全球首个30米分辨率湿地数据集(2000—2022)
数据简介 今天我们分享的数据是全球30米分辨率湿地数据集,包含8种湿地亚类,该数据以0.5X0.5的瓦片存储,我们整理了所有属于中国的瓦片名称与其对应省份,方便大家研究使用。 该数据集作为全球首个30米分辨率、覆盖2000–2022年时间…...

【CSS position 属性】static、relative、fixed、absolute 、sticky详细介绍,多层嵌套定位示例
文章目录 ★ position 的五种类型及基本用法 ★ 一、position 属性概述 二、position 的五种类型详解(初学者版) 1. static(默认值) 2. relative(相对定位) 3. absolute(绝对定位) 4. fixed(固定定位) 5. sticky(粘性定位) 三、定位元素的层级关系(z-i…...

ardupilot 开发环境eclipse 中import 缺少C++
目录 文章目录 目录摘要1.修复过程摘要 本节主要解决ardupilot 开发环境eclipse 中import 缺少C++,无法导入ardupilot代码,会引起查看不方便的问题。如下图所示 1.修复过程 0.安装ubuntu 软件中自带的eclipse 1.打开eclipse—Help—install new software 2.在 Work with中…...
DeepSeek 技术赋能无人农场协同作业:用 AI 重构农田管理 “神经网”
目录 一、引言二、DeepSeek 技术大揭秘2.1 核心架构解析2.2 关键技术剖析 三、智能农业无人农场协同作业现状3.1 发展现状概述3.2 协同作业模式介绍 四、DeepSeek 的 “农场奇妙游”4.1 数据处理与分析4.2 作物生长监测与预测4.3 病虫害防治4.4 农机协同作业调度 五、实际案例大…...

以光量子为例,详解量子获取方式
光量子技术获取量子比特可在室温下进行。该方式有望通过与名为硅光子学(silicon photonics)的光波导(optical waveguide)芯片制造技术和光纤等光通信技术相结合来实现量子计算机。量子力学中,光既是波又是粒子。光子本…...
Python竞赛环境搭建全攻略
Python环境搭建竞赛技术文章大纲 竞赛背景与意义 竞赛的目的与价值Python在竞赛中的应用场景环境搭建对竞赛效率的影响 竞赛环境需求分析 常见竞赛类型(算法、数据分析、机器学习等)不同竞赛对Python版本及库的要求硬件与操作系统的兼容性问题 Pyth…...

软件工程 期末复习
瀑布模型:计划 螺旋模型:风险低 原型模型: 用户反馈 喷泉模型:代码复用 高内聚 低耦合:模块内部功能紧密 模块之间依赖程度小 高内聚:指的是一个模块内部的功能应该紧密相关。换句话说,一个模块应当只实现单一的功能…...

Linux 下 DMA 内存映射浅析
序 系统 I/O 设备驱动程序通常调用其特定子系统的接口为 DMA 分配内存,但最终会调到 DMA 子系统的dma_alloc_coherent()/dma_alloc_attrs() 等接口。 关于 dma_alloc_coherent 接口详细的代码讲解、调用流程,可以参考这篇文章,我觉得写的非常…...