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()过来。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/229354.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

10天玩转Python第9天:python 面向对象 全面详解与代码示例

今日内容 异常 模块和包 导入模块(导包)if __name__ "__main__": Unitest 框架的学习 了解, 基本组成 异常 异常传递[了解] 异常传递是 Python 中已经实现好了,我们不需要操作, 我们知道异常会进行传递. ​ 异常传递: 在函数嵌套调用的过程中, 被调用的函数 ,发…

Tailwind CSS

Tailwind CSS 简介,在这一课中,我们将初步了解下 Tailwind CSS 。 什么是 Tailwind CSS? Tailwind CSS 是一个 CSS 框架,它提供了一系列预定义的实用类,您可以使用这些实用类来快速构建网页。Tailwind CSS 的主要特点是,它可以让您只编写您需要的 CSS,从而减少代码量。…

Grafana Loki 快速尝鲜

Grafana Loki 是一个支持水平扩展、高可用的聚合日志系统&#xff0c;跟其他的聚合日志系统不同&#xff0c;Loki只对日志的元数据-标签进行索引&#xff0c;日志数据会被压缩并存储在对象存储中&#xff0c;甚至可以存储在本地文件系统中&#xff0c;能够有效降低成本&#xf…

下午好~ 我的论文【遥感】(第一期)

写在前面&#xff1a;下午浑浑噩噩&#xff0c;泡杯茶&#xff0c;读篇论文吧 首先说明&#xff0c;时间有限没有那么精力一一回复了&#xff0c;对不起各位了TAT 文章目录 遥感Bi-Dilation-formerCNN-GNN-FusionMulti-hierarchical cross transformerCoupled CNNs 遥感 Bi-D…

NGINX日志配置参数

日志参数 参数说明示例$remote_addr客户端地址219.227.111.255$remote_user客户端用户名称—$time_local访问时间和时区14/Dec/202023:09:27:01 0800$time_iso8601访问时间2023-12-14T09:27:4508:00$request请求的URI和HTTP协议“GET /article-10000.html HTTP/1.1”$request…

Vue3 用 Proxy API 替代 defineProperty API 的那些事

一、Object.defineProperty 定义&#xff1a;Object.defineProperty() 方法会直接在一个对象上定义一个新属性&#xff0c;或者修改一个对象的现有属性&#xff0c;并返回此对象 1.1 为什么能实现响应式 通过defineProperty 两个属性&#xff0c;get及set get 属性的 gett…

电路中的屏蔽罩作用及设计

1.1 屏蔽罩作用 1.1.1 屏蔽电子信号,防止外界的干扰或内部向外的辐射&#xff1a; 一般见于通信类电路PCB&#xff0c;主要一个无线通信产品上有的敏感器件、模拟、数字电路、DCDC电源电路&#xff0c;都需屏蔽隔离&#xff0c;是为了不影响其它电路&#xff0c;也有防止其它电…

国际刑警组织推出新的生物识别系统

2023 年 11 月 29 日&#xff0c;国际刑警组织发布了一份有关创建生物识别工具的新闻稿&#xff0c;至少在意大利&#xff0c;该工具似乎已经陷入沉默&#xff0c;但让我们看看为什么我们会对这个东西感兴趣。 国际刑警组织的新闻稿用了整整一段时间来讨论与隐私相关的问题&am…

0x31 质数

0x31 质数 定义&#xff1a; 若一个正整数无法被除了1和它自身之外的任何自然数整除&#xff0c;则称该数为质数&#xff08;或素数&#xff09;&#xff0c;否则则称该正整数为合数。 在整个自然数集合中&#xff0c;质数的数量不多&#xff0c;分布比较稀疏&#xff0c;对…

【加解密】报文签名与加解密,MD5,RSA,AES使用案例(基于 Java)

需要考虑哪些问题&#xff1f; 在进行报文传输时&#xff0c;有两个问题需要考虑&#xff1a; 消息防篡改加密报文 定义消息结构 为了方便后面使用&#xff0c;这里定义消息结构&#xff1a; public static class Message {public String data; //消息public String sign;…

【Linux】Firewalld防火墙新增端口、开启、查看等

Linux操作系统中&#xff0c;Firewalld防火墙相关操作如下&#xff1a; 安装 yum install firewalld firewalld-configFirewall开启常见端口命令 新增端口&#xff1a; firewall-cmd --zonepublic --add-port80/tcp --permanentfirewall-cmd --zonepublic --add-port443/tc…

动态规划——OJ题(一)

&#x1f4d8;北尘_&#xff1a;个人主页 &#x1f30e;个人专栏:《Linux操作系统》《经典算法试题 》《C》 《数据结构与算法》 ☀️走在路上&#xff0c;不忘来时的初心 文章目录 一、第N个泰波那契数1、题目讲解2、思路讲解3、代码实现 二、三步问题1、题目讲解2、思路讲解…

Maven配置文件setings.xml详解依赖搜索顺序详解

Maven配置文件详解 文章目录 Maven配置文件详解一、settings.xml配置文件的作用二、settings.xml元素详解0.示例1.LocalRepository2.InteractiveMode3.UsePluginRegistry4.Offline5.PluginGroups6.Servers7.Mirrors8.Proxies9.Profiles10.Activation11.properties12.Repositori…

拖拽属性 draggable

H5 新增的属性 draggable&#xff0c;它能够给与一切的 html 元素拖动的效果。 拖拽元素 属性为 draggable"true" 的元素&#xff0c;可拖动&#xff0c;且拖动时鼠标变为禁用图标 ps: 直接写 draggable 可能无效 ondragstart 开始拖拽时触发&#xff08;按下鼠标…

ARM架构简析

全局与局量等知识 断电后&#xff0c;程序以及数据都在FLASH中。 断电后&#xff0c;内存中就没有变量了。 程序在烧在FLASH中的&#xff1b; 程序运行的时候&#xff0c;全局变量的初始值&#xff0c;必然是从FLAASH中的来的&#xff1a; 初始化全局变量的过程&#xff1a;…

【微服务】服务间调用

当我们的应用从一个大单体拆分成多个微服务之后&#xff0c;服务间调用有多少种方式&#xff1f;服务间调用如果出现超时&#xff0c;如果避免雪崩&#xff0c;即如何做限流熔断机制&#xff0c;原理是什么&#xff1f; 服务间调用方式 OpenFeign RestTemplate WebClient …

pytorch和pytorchvision安装

参考https://blog.csdn.net/2301_76863102/article/details/129369549 https://blog.csdn.net/weixin_43798572/article/details/123122477 查看我的版本 右键&#xff0c;nvivdia控制面板&#xff0c;帮助&#xff0c;系统信息 驱动程序版本号为528.49 更新很快的 CUDA版本…

大模型入门1: 指令微调

scaling law大模型评测指令微调 微调7B模型需要328G的显存(SGDMomentum)&#xff0c;至少需要2张A100的显卡才能满足 数据 格式 wget https://raw.githubusercontent.com/baichuan-inc/Baichuan2/main/fine-tune/data/belle_chat_ramdon_10k.json wget https://huggingface…

【C++】基于iomanip标准库的流对象格式化输出详解

一.iomanip标准库是什么&#xff1f;&#xff08;What is it&#xff09; 1.从名字上看&#xff1a;iomanip是 io-manipulator的简称&#xff0c;意思是输入输出操控器 2.从对象上看&#xff1a;io针对的是流对象的输入输出&#xff0c;包括常见的&#xff1a; - 标准输入输出…

cpp:1:10: fatal error: opencv2/core.hpp: 没有那个文件或目录

前言&#xff1a; 我按照官网方法安装了opencv&#xff0c;运行的也是官网的测试代码&#xff1a; #include <opencv2/core.hpp> #include <opencv2/highgui.hpp> using namespace cv; int main() {printf("hello world")return 0; } 半解决&#xff…