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

【MyBatis-Plus 进阶功能】开发中常用场景剖析

MyBatis-Plus(MP)除了封装常见的 CRUD 操作,还提供了一些高级功能,进一步简化复杂场景下的开发工作。本文将逐一讲解 逻辑删除自动填充多表关联查询的原理与使用方式,让你快速掌握这些技巧!


一、逻辑删除

逻辑删除是指在数据库中不直接删除记录,而是通过标记(如 is_deleted 字段)表示数据是否有效。

1. 原理与配置

逻辑删除是指在数据库中不直接删除记录,而是通过标记(如 is_deleted 字段)来表示数据是否有效,查询时会自动过滤掉已标记为“删除”的记录。

2. 配置步骤

  1. 在数据库表中添加逻辑删除字段(如 is_deleted):

    ALTER TABLE user ADD COLUMN is_deleted TINYINT(1) DEFAULT 0 COMMENT '逻辑删除标记';
    
  2. 在实体类中添加逻辑删除注解

    @TableLogic
    private Integer isDeleted;
    
  3. 全局配置逻辑删除行为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;

假设初始数据为空:

idnameis_deleted
② 插入新记录
User user = new User();
user.setName("Tom");
userMapper.insert(user);

执行后,生成的 SQL 为:

INSERT INTO user (name, is_deleted) VALUES ('Tom', 0);
③ 数据库表中新增记录
idnameis_deleted
1Tom0

④ 执行逻辑删除操作
userMapper.deleteById(1);

生成的 SQL 为:

UPDATE user SET is_deleted = 1 WHERE id = 1;
⑤ 数据库表更新后的记录
idnameis_deleted
1Tom1

⑥ 查询数据

默认情况下,逻辑删除记录不会出现在查询结果中:

List<User> users = userMapper.selectList(null);

生成的 SQL 为:

SELECT id, name FROM user WHERE is_deleted = 0;

查询结果:

idname

若想查询所有记录(包括逻辑删除的记录),需要自定义 SQL:

@Select("SELECT * FROM user")
List<User> selectAllUsers();

查询结果:

idnameis_deleted
1Tom1

通过示例可以看出,逻辑删除通过 自动更新 is_deleted 字段动态添加查询条件 实现了逻辑上的“删除”操作,同时保留了数据的可追溯性。

4. 注意事项

  • 逻辑删除仅在 MP 自动生成的 SQL 中生效,自定义 SQL 需手动添加条件。
  • 查询时默认不包含已删除记录,若需查询已删除数据,需使用自定义 SQL。

二、自动填充

自动填充用于在插入或更新记录时,自动为某些字段赋值(如创建时间、更新时间)。

1. 原理与配置

实现自动填充的关键是使用 @TableFieldfill 属性,并结合 MetaObjectHandler 接口完成填充逻辑。

自动填充的核心原理在于 MyBatis-Plus 提供的元对象处理器(MetaObjectHandler)接口。通过实现这个接口,MP 能够在插入或更新数据时自动填充指定字段。这背后实际上是一种 拦截机制:在执行插入或更新操作时,MP 会检测是否有配置的填充字段,如果有,就调用实现类的逻辑为这些字段赋值。

以下我们详细来分析一下:

  1. 自动填充的触发时机
    MP 在执行插入(INSERT)或更新(UPDATE)操作时,会检查实体类中是否有标注了 @TableField(fill = ...) 的字段。如果存在这些字段,则会触发自动填充逻辑
  2. 元对象处理器的作用
    MP 使用 MetaObjectHandler 接口实现了这个逻辑,通过重写其中的 insertFillupdateFill 方法,可以定义插入或更新时如何自动填充字段
  3. 拦截机制
    • MP 会在内部生成的 SQL 语句执行前,通过拦截器将指定字段的值动态插入到 SQL 语句中。
    • 这一过程对开发者透明,减少了重复编写代码的成本。
  4. 配置生效
    • MP 的核心配置会自动扫描所有实现了 MetaObjectHandler 的类,并在执行插入或更新操作时调用相关方法。
    • 无需手动绑定,这也是为什么只需要实现类即可实现自动填充的原因。

2. 配置步骤

  1. 实体类中通过 @TableField(fill = ...) 指定需要填充的字段:

    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;@TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;
    

    FieldFill.INSERT:仅在插入时填充。

    FieldFill.INSERT_UPDATE:在插入和更新时都会填充。

  2. 实现 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);}
    }
    

    strictInsertFillstrictUpdateFill 是 MP 提供的方法,用于安全地为指定字段赋值。

    MP 会自动调用 insertFillupdateFill,而开发者无需显式调用。

3. 使用示例

① 插入数据

插入或更新时,无需手动设置 createTimeupdateTime

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

字段名类型描述
idINT主键
nameVARCHAR(50)用户名
emailVARCHAR(50)邮箱

订单表(order

字段名类型描述
idINT主键
user_idINT用户 ID
order_timeDATETIME下单时间
amountDECIMAL(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 表中有以下记录:

idnameemail
1张三zhangsan@mail.com

order 表中有以下记录:

iduser_idorder_timeamount
112025-01-01 12:00:00100.00
212025-01-01 15:00:00200.00

运行测试后输出:

用户信息:Alice
订单信息:
订单 ID:1
订单金额:100.00
订单 ID:2
订单金额:200.00

相关文章:

【MyBatis-Plus 进阶功能】开发中常用场景剖析

MyBatis-Plus&#xff08;MP&#xff09;除了封装常见的 CRUD 操作&#xff0c;还提供了一些高级功能&#xff0c;进一步简化复杂场景下的开发工作。本文将逐一讲解 逻辑删除、自动填充、多表关联查询的原理与使用方式&#xff0c;让你快速掌握这些技巧&#xff01; 一、逻辑删…...

【C++/控制台】2048小游戏

源代码&#xff1a; #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 命令参数二、输出字段含义&#xff08;一&#xff09;系统信息&#xff08;二&#xff09;任务信息&#xff08;三&#xff09;CPU 信息&#xff08;四&#xff09;内存信息 三、实例解读系统信息任务信息CPU信息内存信息进程列…...

C++ STL 中的 `unordered_map` 和 `unordered_set` 总结

1. unordered_map unordered_map 是一个基于哈希表实现的容器&#xff0c;存储键值对&#xff08;key-value&#xff09;&#xff0c;每个键必须唯一&#xff0c;可以快速插入、删除、查找。 基本特性 存储结构&#xff1a;键值对 (key-value)。键唯一性&#xff1a;每个键在…...

机器学习基础-概率图模型

&#xff08;一阶&#xff09;马尔科夫模型的基本概念 状态、状态转换概率、初始概率 状态转移矩阵的基本概念 隐马尔可夫模型&#xff08;HMM&#xff09;的基本概念 条件随机场&#xff08;CRF&#xff09;的基本概念 实际应用中的马尔科夫性 自然语言处理&#xff1a; 在词性…...

【MySQL】九、表的内外连接

文章目录 前言Ⅰ. 内连接案例&#xff1a;显示SMITH的名字和部门名称 Ⅱ. 外连接1、左外连接案例&#xff1a;查询所有学生的成绩&#xff0c;如果这个学生没有成绩&#xff0c;也要将学生的个人信息显示出来 2、右外连接案例&#xff1a;对stu表和exam表联合查询&#xff0c;把…...

芯片详细讲解,从而区分CPU、MPU、DSP、GPU、FPGA、MCU、SOC、ECU

目录 芯片的概念结构 芯片的派系划分 通用芯片&#xff08;CPU&#xff0c;MPU&#xff0c;GPU&#xff0c;DSP&#xff09; 定制芯片&#xff08;FPGA&#xff0c;ASIC&#xff09; 芯片之上的集成&#xff08;MCU&#xff0c;SOC&#xff0c;ECU&#xff09; 软硬件的匹…...

halcon三维点云数据处理(十)locate_cylinder_3d

目录 一、locate_cylinder_3d例程代码二、gen_binocular_rectification_map函数三、binocular_disparity函数四、自定义函数select_best_candidates五、自定义函数remove_shadowed_regions 一、locate_cylinder_3d例程代码 1、读取或者创建3D形状模型&#xff0c; 2、根据双目…...

vue(2,3), react (16及以上)开发者工具资源

在前端开发的广阔领域中&#xff0c;Vue.js 和 React.js 作为两大主流框架&#xff0c;各自拥有庞大的用户群体和丰富的生态系统。为了帮助开发者更高效地进行调试和开发&#xff0c;Vue Devtools 和 React 开发者工具应运而生&#xff0c;成为这两个框架不可或缺的辅助工具。本…...

2025年华为OD上机考试真题(Java)——整数对最小和

题目&#xff1a; 给定两个整数数组array1、array2&#xff0c;数组元素按升序排列。假设从array1、array2中分别取出一个元素可构成一对元素&#xff0c;现在需要取出k对元素&#xff0c;并对取出的所有元素求和&#xff0c;计算和的最小值。 注意&#xff1a;两对元素如果对应…...

进程间通信——网络通信——UDP

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

【我的 PWN 学习手札】IO_FILE 之 FSOP

FSOP&#xff1a;File Stream Oriented Programming 通过劫持 _IO_list_all 指向伪造的 _IO_FILE_plus&#xff0c;进而调用fake IO_FILE 结构体对象中被伪造的vtable指向的恶意函数。 目录 前言 一、glibc-exit函数浅析 二、FSOP 三、Largebin attack FSOP &#xff08;…...

新兴的开源 AI Agent 智能体全景技术栈

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

统计学习方法(第二版) 概率分布学习

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

淺談Cocos2djs逆向

前言 簡單聊一下cocos2djs手遊的逆向&#xff0c;有任何相關想法歡迎和我討論^^ 一些概念 列出一些個人認為比較有用的概念&#xff1a; Cocos遊戲的兩大開發工具分別是CocosCreator和CocosStudio&#xff0c;區別是前者是cocos2djs專用的開發工具&#xff0c;後者則是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是否勾选 检查是否使用导入时产生的对应混合模式的材质&#xff0c;混合模式不适用默认材质 这里选导入时生成的材质...

el-table自定义按钮控制扩展expand

需求&#xff1a;自定义按钮实现表格扩展内容的展开和收起&#xff0c;实现如下&#xff1a; 将type“expand”的表格列的宽度设置为width"1"&#xff0c;让该操作列不展示出来&#xff0c;然后通过ref动态调用组件的内部方法toggleRowExpansion(row, row.expanded)控…...

opencv CV_TM_SQDIFF未定义标识符

opencv CV_TM_SQDIFF未定义标识符 opencv4部分命名发生变换&#xff0c;将CV_WINDOW_AUTOSIZE改为WINDOW_AUTOSIZE&#xff1b;CV_TM_SQDIFF_NORMED改为TM_SQDIFF_NORMED。...

2024acl论文体悟

总结分析归纳 模型架构与训练方法&#xff1a;一些论文关注于改进大语言模型的架构和训练方法&#xff0c;以提高其性能和效率。例如&#xff0c;“Quantized Side Tuning: Fast and Memory-Efficient Tuning of Quantized Large Language Models”提出了一种量化侧调优方法&a…...

苍穹外卖--缓存菜品

1.问题说明 用户端小程序展示的菜品数据都是通过查询数据库获得&#xff0c;如果用户端访问量比较大&#xff0c;数据库访问压力随之增大 2.实现思路 通过Redis来缓存菜品数据&#xff0c;减少数据库查询操作。 缓存逻辑分析&#xff1a; ①每个分类下的菜品保持一份缓存数据…...

GC1808高性能24位立体声音频ADC芯片解析

1. 芯片概述 GC1808是一款24位立体声音频模数转换器&#xff08;ADC&#xff09;&#xff0c;支持8kHz~96kHz采样率&#xff0c;集成Δ-Σ调制器、数字抗混叠滤波器和高通滤波器&#xff0c;适用于高保真音频采集场景。 2. 核心特性 高精度&#xff1a;24位分辨率&#xff0c…...

Java线上CPU飙高问题排查全指南

一、引言 在Java应用的线上运行环境中&#xff0c;CPU飙高是一个常见且棘手的性能问题。当系统出现CPU飙高时&#xff0c;通常会导致应用响应缓慢&#xff0c;甚至服务不可用&#xff0c;严重影响用户体验和业务运行。因此&#xff0c;掌握一套科学有效的CPU飙高问题排查方法&…...

回溯算法学习

一、电话号码的字母组合 import java.util.ArrayList; import java.util.List;import javax.management.loading.PrivateClassLoader;public class letterCombinations {private static final String[] KEYPAD {"", //0"", //1"abc", //2"…...

无人机侦测与反制技术的进展与应用

国家电网无人机侦测与反制技术的进展与应用 引言 随着无人机&#xff08;无人驾驶飞行器&#xff0c;UAV&#xff09;技术的快速发展&#xff0c;其在商业、娱乐和军事领域的广泛应用带来了新的安全挑战。特别是对于关键基础设施如电力系统&#xff0c;无人机的“黑飞”&…...

Python+ZeroMQ实战:智能车辆状态监控与模拟模式自动切换

目录 关键点 技术实现1 技术实现2 摘要&#xff1a; 本文将介绍如何利用Python和ZeroMQ消息队列构建一个智能车辆状态监控系统。系统能够根据时间策略自动切换驾驶模式&#xff08;自动驾驶、人工驾驶、远程驾驶、主动安全&#xff09;&#xff0c;并通过实时消息推送更新车…...

C++ 设计模式 《小明的奶茶加料风波》

&#x1f468;‍&#x1f393; 模式名称&#xff1a;装饰器模式&#xff08;Decorator Pattern&#xff09; &#x1f466; 小明最近上线了校园奶茶配送功能&#xff0c;业务火爆&#xff0c;大家都在加料&#xff1a; 有的同学要加波霸 &#x1f7e4;&#xff0c;有的要加椰果…...

群晖NAS如何在虚拟机创建飞牛NAS

套件中心下载安装Virtual Machine Manager 创建虚拟机 配置虚拟机 飞牛官网下载 https://iso.liveupdate.fnnas.com/x86_64/trim/fnos-0.9.2-863.iso 群晖NAS如何在虚拟机创建飞牛NAS - 个人信息分享...

LangChain【6】之输出解析器:结构化LLM响应的关键工具

文章目录 一 LangChain输出解析器概述1.1 什么是输出解析器&#xff1f;1.2 主要功能与工作原理1.3 常用解析器类型 二 主要输出解析器类型2.1 Pydantic/Json输出解析器2.2 结构化输出解析器2.3 列表解析器2.4 日期解析器2.5 Json输出解析器2.6 xml输出解析器 三 高级使用技巧3…...

统计学(第8版)——统计抽样学习笔记(考试用)

一、统计抽样的核心内容与问题 研究内容 从总体中科学抽取样本的方法利用样本数据推断总体特征&#xff08;均值、比率、总量&#xff09;控制抽样误差与非抽样误差 解决的核心问题 在成本约束下&#xff0c;用少量样本准确推断总体特征量化估计结果的可靠性&#xff08;置…...