myBatis-plus自动填充插件
在 MyBatis-Plus 3.x 中,自动填充的插件方式发生了变化。现在推荐使用 MetaObjectHandler 接口的实现类来定义字段的填充逻辑。以下是使用 MyBatis-Plus 3.x 自动填充的基本步骤:
1.基本配置
1.1添加 Maven 依赖:
确保你的 Maven 依赖中使用的是 MyBatis-Plus 3.x 版本。
<dependencies><!-- MyBatis-Plus 核心依赖 --><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>最新版本</version></dependency><!-- 其他依赖... -->
</dependencies>
1.2 配置 MyBatis-Plus 自动填充
1.2.1使用@Component注解
创建一个实现 MetaObjectHandler 接口的配置类,用于定义字段填充逻辑。
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;import java.time.LocalDateTime;@Component
public class MyMetaObjectHandler implements MetaObjectHandler {@Overridepublic void insertFill(MetaObject metaObject) {this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());}@Overridepublic void updateFill(MetaObject metaObject) {this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());}
}
这种方式是 MyBatis-Plus 3.x 中推荐的字段自动填充方式。在这个示例中,MyMetaObjectHandler 类上添加了 @Component 注解,确保 Spring Boot 能够自动扫描到并注册为 Bean。这样,MyBatis-Plus 在执行插入和更新操作时会自动调用 MetaObjectHandler 中的对应方法进行字段填充
1.2.1 使用@Bean+@Configuration注解
当然也可以使用@bean的方式在mybatisConfig里面注入
import com.sky.handler.MyMetaObjectHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class MyBatisPlusConfig {@Beanpublic MyMetaObjectHandler myMetaObjectHandler() {return new MyMetaObjectHandler();}
}
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;import java.time.LocalDateTime;@Slf4j
public class MyMetaObjectHandler implements MetaObjectHandler {@Overridepublic void insertFill(MetaObject metaObject) {log.info("开始插入时自动填充");this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());}@Overridepublic void updateFill(MetaObject metaObject) {log.info("开始更新时自动填充");this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());}
}
但是用这种方式的时候需要注意MyMetaObjectHandler这个类前面不能加Component,否则会造成bean冲突。
定义实体类:
在实体类中定义需要自动填充的字段:
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;import java.time.LocalDateTime;@Data
@TableName("your_table_name")
public class YourEntity {@TableIdprivate Long id;private String name;// 其他字段省略...// createTime 和 updateTime 字段将由 MyBatis-Plus 自动填充@TableField(fill = FieldFill.INSERT)private LocalDateTime createTime;@TableField(fill = FieldFill.INSERT_UPDATE)private LocalDateTime updateTime;
}
确保你的实体类中的字段类型和配置的 MetaObjectHandler 中的类型一致。
。
这样达到的实际效果就是
我们在封装好入库的对象的时候,没有setUpdateTime这个属性,那么执行intsert 和 update操作的之后,数据库中也会向UpdateTime存入值。
下面我们需要对一些细节说明一下
2. 严格模式与非严格模式
this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());
在这个示例中,MyMetaObjectHandler 实现了 MetaObjectHandler 接口,并在 insertFill 和 updateFill 方法中定义了字段填充的逻辑。strictInsertFill 和 strictUpdateFill 方法用于严格模式的字段填充。
非严格模式: 在非严格模式下,字段填充通常会在任何情况下都执行,即使字段的值已经被手动设置或者数据库中已经有了一个值。这样可能导致字段值被多次更新,。
严格模式: 相比之下,严格模式会更加谨慎。在严格模式下,字段填充只会在满足一定条件的情况下才执行。例如,在插入操作时,只有在字段的值为 null 时才会进行填充。在更新操作时,只有在字段的值为 null 或者被标记为需要更新时才会进行填充。
3.自动插入的时机
上述的自动填充操作是发生在数据库层面的
具体说是在 MyBatis 执行 SQL 语句时,MyBatis-Plus 框架会拦截这些 SQL 操作,根据配置的自动填充规则来动态生成相应的字段值,然后执行相应的 SQL 操作。这样可以在数据库层面确保这些字段的值符合预期。
插入操作: 当执行插入 SQL 语句时,MyBatis-Plus 拦截器会在插入前执行 MetaObjectHandler 的 insertFill 方法,填充相应字段的值,然后将填充后的 SQL 语句发送给数据库执行。
更新操作: 同理,对于更新 SQL 语句,MyBatis-Plus 会在更新前执行 MetaObjectHandler 的 updateFill 方法,填充相应字段的值,然后将填充后的 SQL 语句发送给数据库执行。
这样做的好处是在数据库层面确保了字段填充的一致性,避免了手动在 Service 层面或者 Controller 层面进行填充,减少了代码冗余和错误的可能性。这也是 MyBatis-Plus 提供的一种便捷的开发方式,使得开发者可以更专注于业务逻辑而不用过多关心数据库层面的操作。
注意:上述操作并不是废话,我们来看这样一个例子(这个例子是我实际项目中的例子,我们只需要关注自动填充相关的部分就可以)
实体类
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@TableName("employee")
public class Employee implements Serializable {private static final long serialVersionUID = 1L;@TableId(type = IdType.AUTO)//自增主键private Long id;@TableField(insertStrategy = FieldStrategy.NOT_EMPTY)private String username;@TableField(insertStrategy = FieldStrategy.NOT_EMPTY)private String name;@TableField(insertStrategy = FieldStrategy.NOT_EMPTY)private String password;@TableField(insertStrategy = FieldStrategy.NOT_EMPTY)private String phone;@TableField(insertStrategy = FieldStrategy.NOT_EMPTY)@EnumValue()private String sex;@TableField(value = "id_number",insertStrategy = FieldStrategy.NOT_NULL)private String idNumber;private EmployeeStatus status;@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")@TableField(fill = FieldFill.INSERT)//表示该字段只会在插入的填充private LocalDateTime createTime;@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")@TableField(fill = FieldFill.INSERT_UPDATE)//表示这个字段只会在传入和更改的时候都会填充private LocalDateTime updateTime;@TableField(fill = FieldFill.INSERT)//表示该字段只会在插入的填充private Long createUser;@TableField(fill = FieldFill.INSERT_UPDATE)//表示该字段只会在插入的填充private Long updateUser;
}}
我的自动填充是这样写的
@Slf4j
public class MyMetaObjectHandler implements MetaObjectHandler {@Overridepublic void insertFill(MetaObject metaObject) {log.info("开始插入时自动填充");this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class,LocalDateTime.now());}@Overridepublic void updateFill(MetaObject metaObject) {log.info("开始更新时自动填充");this.strictUpdateFill(metaObject, "updateTime",LocalDateTime.class, LocalDateTime.of(2022,12,30,5,6,0));}
}
我这里写了一个测试类
@SpringBootTest
class SkyApplicationTest {@ResourceEmployeeMapper employeeMapper;@Testpublic void test() {LambdaUpdateWrapper<Employee> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();lambdaUpdateWrapper.eq(Employee::getName,"张三");lambdaUpdateWrapper.set(Employee::getPhone,"2222222222");Employee employee = new Employee();employee.setPhone("111111111");employeeMapper.update(employee,lambdaUpdateWrapper);System.out.println("");}}
那么这个sql执行的结果是什么?
UPDATEemployee SETphone='111111111',update_time='2022-12-30T05:06',update_user=null,phone='2222222222' WHERE(name = '张三');
这个sql为什么是这样的
我们先看phone这个字段
phone这个字段是没有设置自动填充的,但是两个入参,实体类Employee,和更新条件LambdaUpdateWrapper都对phone都对phone设置了值。
通过sql我们不难发现,mybatis-plus会先根据实体类中不为null的值进行set,然后再写入更新条件中的set
所以,最后更新到数据库里,谁写在SQL语句的最后,数据库里的值就会是谁。
下面
update_time='2022-12-30T05:06',update_user=null,
这两个字段都是自动填充设置的,其中update_time=‘2022-12-30T05:06’,是我设置了固定值,而update_user=null,是因为我没在MyMetaObjectHandler里设置值的原因,才会赋值为null.
那么现在的问题是自动插入的时机在哪呢?
我们不妨构建这样的例子
@Overridepublic void insertFill(MetaObject metaObject) {log.info("开始插入时自动填充");this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class,LocalDateTime.now());this.strictInsertFill(metaObject,"updateUser",Long.class,1L);this.strictInsertFill(metaObject,"createUser",Long.class,1L);}@Overridepublic void updateFill(MetaObject metaObject) {log.info("开始更新时自动填充");this.strictUpdateFill(metaObject, "updateTime",LocalDateTime.class, LocalDateTime.of(2022,12,30,5,6,0));this.strictInsertFill(metaObject,"updateUser",Long.class,1L);}
自动填充的时候,uptdateUser会被填入1L
@Testpublic void test() {LambdaUpdateWrapper<Employee> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();lambdaUpdateWrapper.eq(Employee::getName,"张三");lambdaUpdateWrapper.set(Employee::getUpdateUser,2L);Employee employee = new Employee();employee.setUpdateUser(3L);employeeMapper.update(employee,lambdaUpdateWrapper);System.out.println("");}
而在测试类中。实体类Employee中的updateUser值是3L
查询条件中的updateUser是2L
那么形成的SQL是什么样的呢?
/*17 2023-12-17 16:52:32 */UPDATEemployee SETupdate_time='2022-12-30T05:06',update_user=3,update_user=2 WHERE(name = '张三');
先update_user = 3,后update_user = 2,这个之前就解释过了
但是update = 1怎么没有呢?
这是因为前面说的我们在设置自动填充时遵循的时严格模式的插入,当执行update操作的时候,如果该实体类中的updat_user不为null,就不会触发字段填充。
严格模式下的填充规则
插入时填充规则(INSERT):
当执行插入操作时,只有在实体类的字段的值为 null 时才进行填充。
如果实体类字段的值不为 null,则填充操作会被忽略。
更新时填充规则(UPDATE):
当执行更新操作时,只有在字段的值为 null 或者字段被标记为需要更新时才进行填充。
如果字段的值不为 null,且字段没有被标记为需要更新,填充操作会被忽略。
一定一定注意,是实体类中的字段值为null
我们将测试类更改一下:
@SpringBootTest
class SkyApplicationTest {@ResourceEmployeeMapper employeeMapper;@Testpublic void test() {LambdaUpdateWrapper<Employee> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();lambdaUpdateWrapper.eq(Employee::getName,"张三");lambdaUpdateWrapper.set(Employee::getUpdateUser,2L);Employee employee = new Employee();
// employee.setUpdateUser(3L);employeeMapper.update(employee,lambdaUpdateWrapper);System.out.println("");}}
我们将实体类中updateUser设置为null
你会发现生成的SQL语言是
UPDATEemployee SETupdate_time='2022-12-30T05:06',update_user=1,update_user=2 WHERE(name = '张三');
会发现,自动填充是触发了的。
并且在lambdaUpdateWrapper这个更新条件的前面。这是因为本质上,自动填充是给实体类的update_time赋值的。
此外还有一个注意点,有一种情况也会导致自动填充失效。
在mybatispluss的官网也有说明。
https://baomidou.com/pages/4c6bcf/

看下面这个例子
@SpringBootTest
class SkyApplicationTest {@ResourceEmployeeMapper employeeMapper;@Testpublic void test() {LambdaUpdateWrapper<Employee> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();lambdaUpdateWrapper.eq(Employee::getName,"张三");lambdaUpdateWrapper.set(Employee::getUpdateUser,2L);// Employee employee = new Employee();
// employee.setUpdateUser(3L);employeeMapper.update(null,lambdaUpdateWrapper);System.out.println("");}}
他的sql实际为
UPDATEemployee SETupdate_user=2 WHERE(name = '张三');
之所以会这样,update的实体类入参是null,自动填充根本就没有启动。
所以当我们的实体类在定义的时候使用了@TableField(fill = FieldFill.*****)的时候,使用mybatis-plus自带的update方法的时候一定不能传null的实体类。可以传new my_entity()过来。
相关文章:
myBatis-plus自动填充插件
在 MyBatis-Plus 3.x 中,自动填充的插件方式发生了变化。现在推荐使用 MetaObjectHandler 接口的实现类来定义字段的填充逻辑。以下是使用 MyBatis-Plus 3.x 自动填充的基本步骤: 1.基本配置 1.1添加 Maven 依赖: 确保你的 Maven 依赖中使…...
746. 使用最小花费爬楼梯 --力扣 --JAVA
题目 给你一个整数数组 cost ,其中 cost[i] 是从楼梯第 i 个台阶向上爬需要支付的费用。一旦你支付此费用,即可选择向上爬一个或者两个台阶。 你可以选择从下标为 0 或下标为 1 的台阶开始爬楼梯。 请你计算并返回达到楼梯顶部的最低花费。 解题思路 到…...
使用Verdaccio搭建私有npm仓库
搭建团队的私有仓库,保证团队组件的安全维护和私密性,是进阶前端开发主管路上,必不可少的一项技能。 一、原理 我们平时使用npm publish进行发布时,上传的仓库默认地址是npm,通过Verdaccio工具在本地新建一个仓库地址…...
87 GB 模型种子,GPT-4 缩小版,超越ChatGPT3.5,多平台在线体验
瞬间爆火的Mixtral 8x7B 大家好,我是老章 最近风头最盛的大模型当属Mistral AI 发布的Mixtral 8x7B了,火爆程度压过Google的Gemini。 缘起是MistralAI二话不说,直接在其推特账号上甩出了一个87GB的种子 随后Mixtral公布了模型的一些细节&am…...
Golang 数组 移除元素 双指针法 leetcode27 小记
文章目录 移除元素 leetcode27暴力解法双指针法1. 快慢指针2. 双向指针 移除元素 leetcode27 go中数据类型的分类: 1.值类型:int、float、bool、string、数组、结构体 2.引用类型:指针、切片、map、管道、接口 由于切片为引用类型,…...
c# OpenCV 图像裁剪、调整大小、旋转、透视(三)
图像裁剪、调整大小、旋转、透视图像处理基本操作。 croppedImage 图像裁剪Cv2.Resize() 调整图像大小图像旋转 Cv2.Rotate()旋转Cv2.Flip()翻转Cv2.WarpAffine()任意角度旋转Cv2.GetAffineTransform()透视 一、图像裁剪 // 读取原始图像 Mat image new Mat("1.png&q…...
Kafka相关知识
一、kafka架构 Kafka基础知识 Kafka是最初由Linkedin公司开发,是一个分布式、分区的、多副本的、多生产者、多订阅者,基于zookeeper协 调的分布式日志系统(也可以当做MQ系统),常见可以用于webynginx日志、访问日志,消息服务等等&…...
gitlab 通过svn hook 触发
jenkins 起一个item 配置: 我选的自由风格的 源码管理配置 先选subversion 就是svn类型 url 设置project 的路径, 注意是工程,不是svn 顶层 添加一个账户来进行pull 等操作 选择添加的账号 构建触发器: ,重要的是要自…...
设计模式详解---单例模式
1. 设计模式详解 单例模式是一种创建对象的设计模式,它确保一个类只有一个实例,并提供全局访问点以获取该实例。 在单例模式中,类负责创建自己的唯一实例,并确保任何其他对象只能访问该实例。这对于需要共享状态或资源的情况非常有…...
毕设之-Hlang后端架构-双系统交互
文章目录 前言交互流程基本流程约定公钥人人中台携带公钥获取私钥私钥生成人人中台携带私钥访问私钥验证(博客系统) 调试演示总结 前言 前天我们完成了基本的整合,但是还没有整合到我们的业务系统,也就是博客系统。本来昨天要搞一…...
什么同源策略?
同源 同源指的是URL有相同的协议、主机名和端口号。 同源策略 同源策略指的是浏览器提供的安全功能,非同源的RUL之间不能进行资源交互 跨域 两个非同源之间要进行资源交互就是跨域。 浏览器对跨域请求的拦截 浏览器是允许跨域请求的,但是请求返回…...
破译模式:模式识别在计算机视觉中的作用
一、介绍 在当代数字领域,计算机视觉中的模式识别是关键的基石,推动着众多技术进步和应用。本文探讨了计算机视觉中模式识别的本质、方法、应用、挑战和未来趋势。通过使机器能够识别和解释视觉数据中的模式,模式识别不仅推动了计算机视觉领域…...
c语言-全局变量与局部变量
目录 1、(作用)域的概念 2、全局与局部的相对性 3、生命周期 3、静态变量static 结语: 前言: 在c语言中,全局变量的可见范围是整个工程,而局部变量的可见范围从该变量被定义到该作用域结束,…...
【Spring】00 入门指南
文章目录 1.简介2.概念1)控制反转(IoC)2)依赖注入(DI) 3.核心模块1)Spring Core2)Spring AOP3)Spring MVC4)Spring Data5)Spring Boot 4.编写 Hel…...
BIM 技术:CIM (City Information Modeling) 1-7 级
本心、输入输出、结果 文章目录 BIM 技术:CIM (City Information Modeling) 1-7 级前言城市信息模型(CIM)概述城市信息模型分级介绍CIM 1CIM 2CIM 3CIM 4CIM 5CIM 6CIM 7 花有重开日,人无再少年实践是检验真…...
c++ websocket 协议分析与实现
前言 网上有很多第三方库,nopoll,uwebsockets,libwebsockets,都喜欢回调或太复杂,个人只需要在后端用,所以手动写个; 1:环境 ubuntu18 g(支持c11即可) 第三方库:jsoncpp,openssl 2:安装 jsoncpp 读取json 配置文件 用 自动安装 网…...
kali虚拟机无网络
1.查看虚拟机的网卡模式 在虚拟机设置里,一般选择桥接模式,也可以选择NAT模式。 2、你的IP地址是否写死了(设置为静态IP) vim编辑模式下的命令: 按a或i进入编辑模式,然后按esc键退出编辑模式,s…...
Unity2023.3(Unity6)版本开始将可以发布WebGPU
翻译一段官网上的话: 利用Unity 2023.3(正式发布时应该称为Unity6)中最新的WebGPU图形API集成,尝试最大限度的提升您的网络游戏的真实感。 通过与谷歌的战略合作,Unity实时3D平台的强大的图形功能现在为图形丰富的网络游戏进行微调࿰…...
计算机网络期末考试A卷及答案
一、选择题(30分,每题1分) 世界上第一个网络系统是( C )。 A、ENIAC B、以太网 C、ARPANET D、DECNET 2.在常用的传输介质中,( C )的带宽最宽、信号传输衰减最小、抗干扰能力最强。 A.双绞线 …...
<蓝桥杯软件赛>零基础备赛20周--第10周--二分
报名明年4月蓝桥杯软件赛的同学们,如果你是大一零基础,目前懵懂中,不知该怎么办,可以看看本博客系列:备赛20周合集 20周的完整安排请点击:20周计划 每周发1个博客,共20周(读者可以按…...
CTF show Web 红包题第六弹
提示 1.不是SQL注入 2.需要找关键源码 思路 进入页面发现是一个登录框,很难让人不联想到SQL注入,但提示都说了不是SQL注入,所以就不往这方面想了 先查看一下网页源码,发现一段JavaScript代码,有一个关键类ctfs…...
脑机新手指南(八):OpenBCI_GUI:从环境搭建到数据可视化(下)
一、数据处理与分析实战 (一)实时滤波与参数调整 基础滤波操作 60Hz 工频滤波:勾选界面右侧 “60Hz” 复选框,可有效抑制电网干扰(适用于北美地区,欧洲用户可调整为 50Hz)。 平滑处理&…...
linux 下常用变更-8
1、删除普通用户 查询用户初始UID和GIDls -l /home/ ###家目录中查看UID cat /etc/group ###此文件查看GID删除用户1.编辑文件 /etc/passwd 找到对应的行,YW343:x:0:0::/home/YW343:/bin/bash 2.将标红的位置修改为用户对应初始UID和GID: YW3…...
C++ 求圆面积的程序(Program to find area of a circle)
给定半径r,求圆的面积。圆的面积应精确到小数点后5位。 例子: 输入:r 5 输出:78.53982 解释:由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982,因为我们只保留小数点后 5 位数字。 输…...
多模态大语言模型arxiv论文略读(108)
CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文标题:CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文作者:Sayna Ebrahimi, Sercan O. Arik, Tejas Nama, Tomas Pfister ➡️ 研究机构: Google Cloud AI Re…...
JVM暂停(Stop-The-World,STW)的原因分类及对应排查方案
JVM暂停(Stop-The-World,STW)的完整原因分类及对应排查方案,结合JVM运行机制和常见故障场景整理而成: 一、GC相关暂停 1. 安全点(Safepoint)阻塞 现象:JVM暂停但无GC日志,日志显示No GCs detected。原因:JVM等待所有线程进入安全点(如…...
JS设计模式(4):观察者模式
JS设计模式(4):观察者模式 一、引入 在开发中,我们经常会遇到这样的场景:一个对象的状态变化需要自动通知其他对象,比如: 电商平台中,商品库存变化时需要通知所有订阅该商品的用户;新闻网站中࿰…...
【Linux】Linux 系统默认的目录及作用说明
博主介绍:✌全网粉丝23W,CSDN博客专家、Java领域优质创作者,掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域✌ 技术范围:SpringBoot、SpringCloud、Vue、SSM、HTML、Nodejs、Python、MySQL、PostgreSQL、大数据、物…...
C#学习第29天:表达式树(Expression Trees)
目录 什么是表达式树? 核心概念 1.表达式树的构建 2. 表达式树与Lambda表达式 3.解析和访问表达式树 4.动态条件查询 表达式树的优势 1.动态构建查询 2.LINQ 提供程序支持: 3.性能优化 4.元数据处理 5.代码转换和重写 适用场景 代码复杂性…...
tauri项目,如何在rust端读取电脑环境变量
如果想在前端通过调用来获取环境变量的值,可以通过标准的依赖: std::env::var(name).ok() 想在前端通过调用来获取,可以写一个command函数: #[tauri::command] pub fn get_env_var(name: String) -> Result<String, Stri…...
